mirror of
https://github.com/open62541/open62541.git
synced 2026-10-17 04:42:09 +08:00
335cead676
Use pointer-sized atomic run flags in threaded fixtures, including dynamically allocated discovery and browse flags. Pointer sizing supports the MSVC atomic implementation as well as C11 and GCC atomics. Serialize the client error-injection test with the server network callbacks before accessing the internal session and shared send buffer.
731 lines
27 KiB
C
731 lines
27 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/. */
|
|
|
|
/* Client highlevel write, browse and method call tests */
|
|
#ifndef _GNU_SOURCE
|
|
# define _GNU_SOURCE
|
|
#endif
|
|
|
|
#include <open62541/client.h>
|
|
#include <open62541/client_config_default.h>
|
|
#include <open62541/client_highlevel.h>
|
|
#include <open62541/client_subscriptions.h>
|
|
#include <open62541/server.h>
|
|
#include <open62541/server_config_default.h>
|
|
#include <open62541/types.h>
|
|
#include <check.h>
|
|
#include "testing_clock.h"
|
|
#include "thread_wrapper.h"
|
|
#include "test_helpers.h"
|
|
|
|
static UA_Server *server;
|
|
static UA_atomic(uintptr_t) running;
|
|
static THREAD_HANDLE server_thread;
|
|
|
|
THREAD_CALLBACK(serverloop) {
|
|
while(UA_atomic_load(&running))
|
|
UA_Server_run_iterate(server, true);
|
|
return 0;
|
|
}
|
|
|
|
static void setup(void) {
|
|
server = UA_Server_newForUnitTest();
|
|
ck_assert(server != NULL);
|
|
|
|
/* Add a method node for testing */
|
|
UA_MethodAttributes methAttr = UA_MethodAttributes_default;
|
|
methAttr.displayName = UA_LOCALIZEDTEXT("en", "TestMethod");
|
|
methAttr.executable = true;
|
|
methAttr.userExecutable = true;
|
|
UA_Server_addMethodNode(server,
|
|
UA_NODEID_NUMERIC(1, 62000),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
|
|
UA_QUALIFIEDNAME(1, "TestMethod"),
|
|
methAttr, NULL, 0, NULL, 0, NULL, NULL, NULL);
|
|
|
|
/* Add a fully writable variable for writing tests */
|
|
UA_VariableAttributes varAttr = UA_VariableAttributes_default;
|
|
UA_Int32 val = 42;
|
|
UA_Variant_setScalar(&varAttr.value, &val, &UA_TYPES[UA_TYPES_INT32]);
|
|
varAttr.displayName = UA_LOCALIZEDTEXT("en", "WritableVar");
|
|
varAttr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE |
|
|
UA_ACCESSLEVELMASK_STATUSWRITE | UA_ACCESSLEVELMASK_TIMESTAMPWRITE;
|
|
varAttr.writeMask = UA_WRITEMASK_DISPLAYNAME | UA_WRITEMASK_DESCRIPTION |
|
|
UA_WRITEMASK_WRITEMASK | UA_WRITEMASK_ACCESSLEVEL |
|
|
UA_WRITEMASK_MINIMUMSAMPLINGINTERVAL | UA_WRITEMASK_HISTORIZING |
|
|
UA_WRITEMASK_VALUERANK | UA_WRITEMASK_ARRAYDIMENSIONS |
|
|
UA_WRITEMASK_DATATYPE;
|
|
UA_Server_addVariableNode(server,
|
|
UA_NODEID_NUMERIC(1, 62001),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
|
|
UA_QUALIFIEDNAME(1, "WritableVar"),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
|
|
varAttr, NULL, NULL);
|
|
|
|
UA_atomic_store(&running, true);
|
|
UA_Server_run_startup(server);
|
|
THREAD_CREATE(server_thread, serverloop);
|
|
}
|
|
|
|
static void teardown(void) {
|
|
UA_atomic_store(&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);
|
|
}
|
|
|
|
/* === Additional write attribute tests === */
|
|
START_TEST(hl_writeWriteMask) {
|
|
UA_Client *client = connectClient();
|
|
UA_UInt32 wm = UA_WRITEMASK_DISPLAYNAME;
|
|
UA_StatusCode res = UA_Client_writeWriteMaskAttribute(client,
|
|
UA_NODEID_NUMERIC(1, 62001), &wm);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
START_TEST(hl_writeAccessLevel) {
|
|
UA_Client *client = connectClient();
|
|
UA_Byte al = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
|
|
UA_StatusCode res = UA_Client_writeAccessLevelAttribute(client,
|
|
UA_NODEID_NUMERIC(1, 62001), &al);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
START_TEST(hl_writeRbacAttributes) {
|
|
UA_Client *client = connectClient();
|
|
UA_Variant permissions;
|
|
UA_RolePermissionType permission;
|
|
UA_RolePermissionType_init(&permission);
|
|
permission.roleId = UA_NODEID_NUMERIC(0, UA_NS0ID_WELLKNOWNROLE_ANONYMOUS);
|
|
permission.permissions = UA_PERMISSIONTYPE_BROWSE;
|
|
UA_Variant_setArray(&permissions, &permission, 1,
|
|
&UA_TYPES[UA_TYPES_ROLEPERMISSIONTYPE]);
|
|
UA_StatusCode res = UA_Client_writeRolePermissionsAttribute(client,
|
|
UA_NODEID_NUMERIC(1, 62001), &permissions);
|
|
ck_assert(UA_StatusCode_isBad(res));
|
|
|
|
UA_AccessRestrictionType restrictions = UA_ACCESSRESTRICTIONTYPE_NONE;
|
|
res = UA_Client_writeAccessRestrictionsAttribute(client,
|
|
UA_NODEID_NUMERIC(1, 62001), &restrictions);
|
|
ck_assert(UA_StatusCode_isBad(res));
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
START_TEST(hl_writeMinSampling) {
|
|
UA_Client *client = connectClient();
|
|
UA_Double val = 100.0;
|
|
UA_StatusCode res = UA_Client_writeMinimumSamplingIntervalAttribute(client,
|
|
UA_NODEID_NUMERIC(1, 62001), &val);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
START_TEST(hl_writeHistorizing) {
|
|
UA_Client *client = connectClient();
|
|
UA_Boolean val = false;
|
|
UA_StatusCode res = UA_Client_writeHistorizingAttribute(client,
|
|
UA_NODEID_NUMERIC(1, 62001), &val);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
START_TEST(hl_writeValueRank) {
|
|
UA_Client *client = connectClient();
|
|
UA_Int32 val = UA_VALUERANK_SCALAR;
|
|
UA_StatusCode res = UA_Client_writeValueRankAttribute(client,
|
|
UA_NODEID_NUMERIC(1, 62001), &val);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
START_TEST(hl_writeValueScalar) {
|
|
UA_Client *client = connectClient();
|
|
UA_Int32 val = 999;
|
|
UA_StatusCode res = UA_Client_writeValueAttribute_scalar(client,
|
|
UA_NODEID_NUMERIC(1, 62001), &val, &UA_TYPES[UA_TYPES_INT32]);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
START_TEST(hl_writeValueEx) {
|
|
UA_Client *client = connectClient();
|
|
UA_DataValue dv;
|
|
UA_DataValue_init(&dv);
|
|
dv.hasValue = true;
|
|
UA_Int32 val = 777;
|
|
UA_Variant_setScalar(&dv.value, &val, &UA_TYPES[UA_TYPES_INT32]);
|
|
dv.hasSourceTimestamp = true;
|
|
dv.sourceTimestamp = UA_DateTime_now();
|
|
UA_StatusCode res = UA_Client_writeValueAttributeEx(client,
|
|
UA_NODEID_NUMERIC(1, 62001), &dv);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
/* === Add various node types === */
|
|
START_TEST(hl_addVariableTypeNode) {
|
|
UA_Client *client = connectClient();
|
|
UA_NodeId outNodeId;
|
|
UA_VariableTypeAttributes attr = UA_VariableTypeAttributes_default;
|
|
attr.displayName = UA_LOCALIZEDTEXT("en", "TestVarType");
|
|
UA_StatusCode res = UA_Client_addVariableTypeNode(client,
|
|
UA_NODEID_NUMERIC(1, 62100),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
|
|
UA_QUALIFIEDNAME(1, "TestVarType"),
|
|
attr, &outNodeId);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
START_TEST(hl_addObjectTypeNode) {
|
|
UA_Client *client = connectClient();
|
|
UA_NodeId outNodeId;
|
|
UA_ObjectTypeAttributes attr = UA_ObjectTypeAttributes_default;
|
|
attr.displayName = UA_LOCALIZEDTEXT("en", "TestObjType");
|
|
UA_StatusCode res = UA_Client_addObjectTypeNode(client,
|
|
UA_NODEID_NUMERIC(1, 62101),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
|
|
UA_QUALIFIEDNAME(1, "TestObjType"),
|
|
attr, &outNodeId);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
START_TEST(hl_addReferenceTypeNode) {
|
|
UA_Client *client = connectClient();
|
|
UA_NodeId outNodeId;
|
|
UA_ReferenceTypeAttributes attr = UA_ReferenceTypeAttributes_default;
|
|
attr.displayName = UA_LOCALIZEDTEXT("en", "TestRefType");
|
|
attr.isAbstract = false;
|
|
attr.symmetric = false;
|
|
UA_StatusCode res = UA_Client_addReferenceTypeNode(client,
|
|
UA_NODEID_NUMERIC(1, 62102),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_NONHIERARCHICALREFERENCES),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
|
|
UA_QUALIFIEDNAME(1, "TestRefType"),
|
|
attr, &outNodeId);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
START_TEST(hl_addDataTypeNode) {
|
|
UA_Client *client = connectClient();
|
|
UA_NodeId outNodeId;
|
|
UA_DataTypeAttributes attr = UA_DataTypeAttributes_default;
|
|
attr.displayName = UA_LOCALIZEDTEXT("en", "TestDataType");
|
|
UA_StatusCode res = UA_Client_addDataTypeNode(client,
|
|
UA_NODEID_NUMERIC(1, 62103),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_STRUCTURE),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
|
|
UA_QUALIFIEDNAME(1, "TestDataType"),
|
|
attr, &outNodeId);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
START_TEST(hl_addViewNode) {
|
|
UA_Client *client = connectClient();
|
|
UA_NodeId outNodeId;
|
|
UA_ViewAttributes attr = UA_ViewAttributes_default;
|
|
attr.displayName = UA_LOCALIZEDTEXT("en", "TestView");
|
|
UA_StatusCode res = UA_Client_addViewNode(client,
|
|
UA_NODEID_NUMERIC(1, 62104),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_VIEWSFOLDER),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
|
|
UA_QUALIFIEDNAME(1, "TestView"),
|
|
attr, &outNodeId);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
/* === Delete reference === */
|
|
START_TEST(hl_deleteReference) {
|
|
UA_Client *client = connectClient();
|
|
/* Add node with reference, then delete the reference */
|
|
UA_ObjectAttributes attr = UA_ObjectAttributes_default;
|
|
UA_NodeId newNodeId;
|
|
UA_Client_addObjectNode(client,
|
|
UA_NODEID_NUMERIC(1, 62110),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
|
|
UA_QUALIFIEDNAME(1, "DelRefTest"),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),
|
|
attr, &newNodeId);
|
|
|
|
UA_StatusCode res = UA_Client_deleteReference(client,
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), true,
|
|
UA_EXPANDEDNODEID_NUMERIC(1, 62110), true);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
/* === Method call === */
|
|
START_TEST(hl_callMethod) {
|
|
UA_Client *client = connectClient();
|
|
/* Call GetMonitoredItems - a server-provided method */
|
|
UA_Variant input;
|
|
UA_UInt32 subId = 0;
|
|
UA_Variant_setScalar(&input, &subId, &UA_TYPES[UA_TYPES_UINT32]);
|
|
|
|
size_t outputSize = 0;
|
|
UA_Variant *output = NULL;
|
|
UA_StatusCode res = UA_Client_call(client,
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER),
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_GETMONITOREDITEMS),
|
|
1, &input, &outputSize, &output);
|
|
/* May fail if subscription doesn't exist */
|
|
(void)res;
|
|
if(output)
|
|
UA_Array_delete(output, outputSize, &UA_TYPES[UA_TYPES_VARIANT]);
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
/* === Browse and translate === */
|
|
START_TEST(hl_browseNext) {
|
|
UA_Client *client = connectClient();
|
|
/* Browse with limited results to get continuation point */
|
|
UA_BrowseRequest bReq;
|
|
UA_BrowseRequest_init(&bReq);
|
|
bReq.requestedMaxReferencesPerNode = 1;
|
|
bReq.nodesToBrowseSize = 1;
|
|
bReq.nodesToBrowse = UA_BrowseDescription_new();
|
|
bReq.nodesToBrowse[0].nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ROOTFOLDER);
|
|
bReq.nodesToBrowse[0].resultMask = UA_BROWSERESULTMASK_ALL;
|
|
bReq.nodesToBrowse[0].browseDirection = UA_BROWSEDIRECTION_FORWARD;
|
|
|
|
UA_BrowseResponse bResp = UA_Client_Service_browse(client, bReq);
|
|
ck_assert_uint_eq(bResp.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
|
|
|
|
/* If continuation point exists, browse next */
|
|
if(bResp.resultsSize > 0 && bResp.results[0].continuationPoint.length > 0) {
|
|
UA_BrowseNextRequest bnReq;
|
|
UA_BrowseNextRequest_init(&bnReq);
|
|
bnReq.releaseContinuationPoints = false;
|
|
bnReq.continuationPointsSize = 1;
|
|
bnReq.continuationPoints = &bResp.results[0].continuationPoint;
|
|
|
|
UA_BrowseNextResponse bnResp =
|
|
UA_Client_Service_browseNext(client, bnReq);
|
|
ck_assert_uint_eq(bnResp.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
|
|
|
|
/* Release continuation point */
|
|
bnReq.releaseContinuationPoints = true;
|
|
UA_BrowseNextResponse bnResp2 =
|
|
UA_Client_Service_browseNext(client, bnReq);
|
|
(void)bnResp2;
|
|
|
|
/* Avoid double-free */
|
|
bnReq.continuationPoints = NULL;
|
|
bnReq.continuationPointsSize = 0;
|
|
UA_BrowseNextRequest_clear(&bnReq);
|
|
UA_BrowseNextResponse_clear(&bnResp);
|
|
UA_BrowseNextResponse_clear(&bnResp2);
|
|
}
|
|
|
|
UA_BrowseRequest_clear(&bReq);
|
|
UA_BrowseResponse_clear(&bResp);
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
START_TEST(hl_translateBrowsePath) {
|
|
UA_Client *client = connectClient();
|
|
|
|
UA_BrowsePath bp;
|
|
UA_BrowsePath_init(&bp);
|
|
bp.startingNode = UA_NODEID_NUMERIC(0, UA_NS0ID_ROOTFOLDER);
|
|
UA_RelativePathElement rpe;
|
|
UA_RelativePathElement_init(&rpe);
|
|
rpe.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
|
|
rpe.includeSubtypes = true;
|
|
rpe.targetName = UA_QUALIFIEDNAME(0, "Objects");
|
|
bp.relativePath.elements = &rpe;
|
|
bp.relativePath.elementsSize = 1;
|
|
|
|
UA_TranslateBrowsePathsToNodeIdsRequest tbReq;
|
|
UA_TranslateBrowsePathsToNodeIdsRequest_init(&tbReq);
|
|
tbReq.browsePaths = &bp;
|
|
tbReq.browsePathsSize = 1;
|
|
|
|
UA_TranslateBrowsePathsToNodeIdsResponse tbResp =
|
|
UA_Client_Service_translateBrowsePathsToNodeIds(client, tbReq);
|
|
ck_assert_uint_eq(tbResp.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
|
|
ck_assert_uint_gt(tbResp.resultsSize, 0);
|
|
ck_assert_uint_eq(tbResp.results[0].statusCode, UA_STATUSCODE_GOOD);
|
|
|
|
/* Prevent cleanup of stack-allocated data */
|
|
tbReq.browsePaths = NULL;
|
|
tbReq.browsePathsSize = 0;
|
|
UA_TranslateBrowsePathsToNodeIdsRequest_clear(&tbReq);
|
|
UA_TranslateBrowsePathsToNodeIdsResponse_clear(&tbResp);
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
START_TEST(hl_namespaceGetIndex) {
|
|
UA_Client *client = connectClient();
|
|
UA_UInt16 nsIdx = 0;
|
|
UA_String nsUri = UA_STRING("http://opcfoundation.org/UA/");
|
|
UA_StatusCode res = UA_Client_NamespaceGetIndex(client, &nsUri, &nsIdx);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
ck_assert_uint_eq(nsIdx, 0);
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
/* === forEachChildNodeCall === */
|
|
static UA_StatusCode
|
|
childCallback(UA_NodeId childId, UA_Boolean isInverse,
|
|
UA_NodeId referenceTypeId, void *handle) {
|
|
UA_UInt32 *count = (UA_UInt32 *)handle;
|
|
(*count)++;
|
|
return UA_STATUSCODE_GOOD;
|
|
}
|
|
|
|
START_TEST(hl_forEachChildNode) {
|
|
UA_Client *client = connectClient();
|
|
UA_UInt32 count = 0;
|
|
UA_StatusCode res = UA_Client_forEachChildNodeCall(client,
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), childCallback, &count);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
ck_assert_uint_gt(count, 0);
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
/* === Read additional attributes === */
|
|
START_TEST(hl_readArrayDimensions) {
|
|
UA_Client *client = connectClient();
|
|
UA_UInt32 *dims = NULL;
|
|
size_t dimsSize = 0;
|
|
UA_StatusCode res = UA_Client_readArrayDimensionsAttribute(client,
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME),
|
|
&dimsSize, &dims);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
if(dims)
|
|
UA_Array_delete(dims, dimsSize, &UA_TYPES[UA_TYPES_UINT32]);
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
START_TEST(hl_readExecutable) {
|
|
UA_Client *client = connectClient();
|
|
UA_Boolean val;
|
|
UA_StatusCode res = UA_Client_readExecutableAttribute(client,
|
|
UA_NODEID_NUMERIC(1, 62000), &val);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
ck_assert(val == true);
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
START_TEST(hl_readUserExecutable) {
|
|
UA_Client *client = connectClient();
|
|
UA_Boolean val;
|
|
UA_StatusCode res = UA_Client_readUserExecutableAttribute(client,
|
|
UA_NODEID_NUMERIC(1, 62000), &val);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
START_TEST(hl_readUserAccessLevel) {
|
|
UA_Client *client = connectClient();
|
|
UA_Byte val;
|
|
UA_StatusCode res = UA_Client_readUserAccessLevelAttribute(client,
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME), &val);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
#ifdef UA_ENABLE_SUBSCRIPTIONS
|
|
/* === Subscription tests === */
|
|
static void dataChangeCallback(UA_Client *client, UA_UInt32 subId,
|
|
void *subContext, UA_UInt32 monId,
|
|
void *monContext, UA_DataValue *value) {
|
|
/* Callback for data change notification */
|
|
(void)client; (void)subId; (void)subContext;
|
|
(void)monId; (void)monContext; (void)value;
|
|
}
|
|
|
|
START_TEST(hl_subscription_create_delete) {
|
|
UA_Client *client = connectClient();
|
|
|
|
UA_CreateSubscriptionRequest subReq = UA_CreateSubscriptionRequest_default();
|
|
UA_CreateSubscriptionResponse subResp =
|
|
UA_Client_Subscriptions_create(client, subReq, NULL, NULL, NULL);
|
|
ck_assert_uint_eq(subResp.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
|
|
UA_UInt32 subId = subResp.subscriptionId;
|
|
|
|
/* Delete subscription */
|
|
UA_StatusCode res = UA_Client_Subscriptions_deleteSingle(client, subId);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
START_TEST(hl_subscription_modify) {
|
|
UA_Client *client = connectClient();
|
|
|
|
UA_CreateSubscriptionRequest subReq = UA_CreateSubscriptionRequest_default();
|
|
UA_CreateSubscriptionResponse subResp =
|
|
UA_Client_Subscriptions_create(client, subReq, NULL, NULL, NULL);
|
|
ck_assert_uint_eq(subResp.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
|
|
UA_UInt32 subId = subResp.subscriptionId;
|
|
|
|
/* Modify */
|
|
UA_ModifySubscriptionRequest modReq;
|
|
UA_ModifySubscriptionRequest_init(&modReq);
|
|
modReq.subscriptionId = subId;
|
|
modReq.requestedPublishingInterval = 500.0;
|
|
modReq.requestedMaxKeepAliveCount = 10;
|
|
modReq.requestedLifetimeCount = 100;
|
|
modReq.maxNotificationsPerPublish = 100;
|
|
UA_ModifySubscriptionResponse modResp =
|
|
UA_Client_Subscriptions_modify(client, modReq);
|
|
ck_assert_uint_eq(modResp.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
|
|
|
|
UA_Client_Subscriptions_deleteSingle(client, subId);
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
START_TEST(hl_monitored_item_create) {
|
|
UA_Client *client = connectClient();
|
|
|
|
UA_CreateSubscriptionRequest subReq = UA_CreateSubscriptionRequest_default();
|
|
UA_CreateSubscriptionResponse subResp =
|
|
UA_Client_Subscriptions_create(client, subReq, NULL, NULL, NULL);
|
|
ck_assert_uint_eq(subResp.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
|
|
UA_UInt32 subId = subResp.subscriptionId;
|
|
|
|
/* Create a monitored item */
|
|
UA_MonitoredItemCreateRequest monReq =
|
|
UA_MonitoredItemCreateRequest_default(
|
|
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME));
|
|
UA_MonitoredItemCreateResult monResp =
|
|
UA_Client_MonitoredItems_createDataChange(client, subId,
|
|
UA_TIMESTAMPSTORETURN_BOTH, monReq,
|
|
NULL, dataChangeCallback, NULL);
|
|
ck_assert_uint_eq(monResp.statusCode, UA_STATUSCODE_GOOD);
|
|
|
|
/* Run a few iterations to get data */
|
|
UA_Client_run_iterate(client, 100);
|
|
UA_Client_run_iterate(client, 100);
|
|
|
|
UA_Client_Subscriptions_deleteSingle(client, subId);
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
START_TEST(hl_subscription_setPublishingMode) {
|
|
UA_Client *client = connectClient();
|
|
|
|
UA_CreateSubscriptionRequest subReq = UA_CreateSubscriptionRequest_default();
|
|
UA_CreateSubscriptionResponse subResp =
|
|
UA_Client_Subscriptions_create(client, subReq, NULL, NULL, NULL);
|
|
ck_assert_uint_eq(subResp.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
|
|
UA_UInt32 subId = subResp.subscriptionId;
|
|
|
|
/* Disable publishing */
|
|
UA_SetPublishingModeRequest spmReq;
|
|
UA_SetPublishingModeRequest_init(&spmReq);
|
|
spmReq.publishingEnabled = false;
|
|
spmReq.subscriptionIds = &subId;
|
|
spmReq.subscriptionIdsSize = 1;
|
|
UA_SetPublishingModeResponse spmResp =
|
|
UA_Client_Subscriptions_setPublishingMode(client, spmReq);
|
|
ck_assert_uint_eq(spmResp.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
|
|
spmReq.subscriptionIds = NULL;
|
|
spmReq.subscriptionIdsSize = 0;
|
|
UA_SetPublishingModeResponse_clear(&spmResp);
|
|
|
|
/* Re-enable */
|
|
spmReq.publishingEnabled = true;
|
|
spmReq.subscriptionIds = &subId;
|
|
spmReq.subscriptionIdsSize = 1;
|
|
spmResp = UA_Client_Subscriptions_setPublishingMode(client, spmReq);
|
|
ck_assert_uint_eq(spmResp.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
|
|
spmReq.subscriptionIds = NULL;
|
|
spmReq.subscriptionIdsSize = 0;
|
|
UA_SetPublishingModeResponse_clear(&spmResp);
|
|
|
|
UA_Client_Subscriptions_deleteSingle(client, subId);
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
#endif /* UA_ENABLE_SUBSCRIPTIONS */
|
|
|
|
/* === Generic read/write === */
|
|
START_TEST(hl_genericRead) {
|
|
UA_Client *client = connectClient();
|
|
|
|
UA_ReadValueId rvi;
|
|
UA_ReadValueId_init(&rvi);
|
|
rvi.nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME);
|
|
rvi.attributeId = UA_ATTRIBUTEID_VALUE;
|
|
|
|
UA_DataValue dv = UA_Client_read(client, &rvi);
|
|
ck_assert(dv.hasValue);
|
|
UA_DataValue_clear(&dv);
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
START_TEST(hl_genericWrite) {
|
|
UA_Client *client = connectClient();
|
|
|
|
UA_WriteValue wv;
|
|
UA_WriteValue_init(&wv);
|
|
wv.nodeId = UA_NODEID_NUMERIC(1, 62001);
|
|
wv.attributeId = UA_ATTRIBUTEID_VALUE;
|
|
wv.value.hasValue = true;
|
|
UA_Int32 val = 555;
|
|
UA_Variant_setScalar(&wv.value.value, &val, &UA_TYPES[UA_TYPES_INT32]);
|
|
|
|
UA_StatusCode res = UA_Client_write(client, &wv);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
disconnectClient(client);
|
|
} END_TEST
|
|
|
|
/* === Client state and endpoints === */
|
|
START_TEST(hl_getEndpoints) {
|
|
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_uint_gt(endpointsSize, 0);
|
|
UA_Array_delete(endpoints, endpointsSize,
|
|
&UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]);
|
|
UA_Client_delete(client);
|
|
} END_TEST
|
|
|
|
START_TEST(hl_findServers) {
|
|
UA_Client *client = UA_Client_newForUnitTest();
|
|
UA_ApplicationDescription *apps = NULL;
|
|
size_t appsSize = 0;
|
|
UA_StatusCode res = UA_Client_findServers(client,
|
|
"opc.tcp://localhost:4840", 0, NULL, 0, NULL, &appsSize, &apps);
|
|
ck_assert_uint_eq(res, UA_STATUSCODE_GOOD);
|
|
ck_assert_uint_gt(appsSize, 0);
|
|
UA_Array_delete(apps, appsSize,
|
|
&UA_TYPES[UA_TYPES_APPLICATIONDESCRIPTION]);
|
|
UA_Client_delete(client);
|
|
} END_TEST
|
|
|
|
START_TEST(hl_connectDisconnect) {
|
|
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);
|
|
|
|
/* Check state */
|
|
UA_SecureChannelState scState;
|
|
UA_SessionState sessState;
|
|
UA_StatusCode connectStatus;
|
|
UA_Client_getState(client, &scState, &sessState, &connectStatus);
|
|
ck_assert_uint_eq(connectStatus, UA_STATUSCODE_GOOD);
|
|
|
|
/* Disconnect */
|
|
UA_Client_disconnect(client);
|
|
UA_Client_getState(client, &scState, &sessState, &connectStatus);
|
|
|
|
UA_Client_delete(client);
|
|
} END_TEST
|
|
|
|
START_TEST(hl_connectFailed) {
|
|
UA_Client *client = UA_Client_newForUnitTest();
|
|
UA_ClientConfig *cc = UA_Client_getConfig(client);
|
|
cc->timeout = 1000;
|
|
UA_StatusCode res = UA_Client_connect(client, "opc.tcp://localhost:54321");
|
|
ck_assert_uint_ne(res, UA_STATUSCODE_GOOD);
|
|
UA_Client_delete(client);
|
|
} END_TEST
|
|
|
|
static Suite *testSuite_clientHL2(void) {
|
|
TCase *tc_write = tcase_create("HL_Write2");
|
|
tcase_add_checked_fixture(tc_write, setup, teardown);
|
|
tcase_add_test(tc_write, hl_writeWriteMask);
|
|
tcase_add_test(tc_write, hl_writeAccessLevel);
|
|
tcase_add_test(tc_write, hl_writeRbacAttributes);
|
|
tcase_add_test(tc_write, hl_writeMinSampling);
|
|
tcase_add_test(tc_write, hl_writeHistorizing);
|
|
tcase_add_test(tc_write, hl_writeValueRank);
|
|
tcase_add_test(tc_write, hl_writeValueScalar);
|
|
tcase_add_test(tc_write, hl_writeValueEx);
|
|
tcase_add_test(tc_write, hl_genericRead);
|
|
tcase_add_test(tc_write, hl_genericWrite);
|
|
|
|
TCase *tc_nodes = tcase_create("HL_NodeTypes");
|
|
tcase_add_checked_fixture(tc_nodes, setup, teardown);
|
|
tcase_add_test(tc_nodes, hl_addVariableTypeNode);
|
|
tcase_add_test(tc_nodes, hl_addObjectTypeNode);
|
|
tcase_add_test(tc_nodes, hl_addReferenceTypeNode);
|
|
tcase_add_test(tc_nodes, hl_addDataTypeNode);
|
|
tcase_add_test(tc_nodes, hl_addViewNode);
|
|
tcase_add_test(tc_nodes, hl_deleteReference);
|
|
|
|
TCase *tc_browse = tcase_create("HL_Browse2");
|
|
tcase_add_checked_fixture(tc_browse, setup, teardown);
|
|
tcase_add_test(tc_browse, hl_browseNext);
|
|
tcase_add_test(tc_browse, hl_translateBrowsePath);
|
|
tcase_add_test(tc_browse, hl_namespaceGetIndex);
|
|
tcase_add_test(tc_browse, hl_forEachChildNode);
|
|
tcase_add_test(tc_browse, hl_callMethod);
|
|
|
|
TCase *tc_read = tcase_create("HL_Read2");
|
|
tcase_add_checked_fixture(tc_read, setup, teardown);
|
|
tcase_add_test(tc_read, hl_readArrayDimensions);
|
|
tcase_add_test(tc_read, hl_readExecutable);
|
|
tcase_add_test(tc_read, hl_readUserExecutable);
|
|
tcase_add_test(tc_read, hl_readUserAccessLevel);
|
|
|
|
#ifdef UA_ENABLE_SUBSCRIPTIONS
|
|
TCase *tc_sub = tcase_create("HL_Subscriptions");
|
|
tcase_add_checked_fixture(tc_sub, setup, teardown);
|
|
tcase_add_test(tc_sub, hl_subscription_create_delete);
|
|
tcase_add_test(tc_sub, hl_subscription_modify);
|
|
tcase_add_test(tc_sub, hl_monitored_item_create);
|
|
tcase_add_test(tc_sub, hl_subscription_setPublishingMode);
|
|
#endif
|
|
|
|
TCase *tc_conn = tcase_create("HL_Connect");
|
|
tcase_add_checked_fixture(tc_conn, setup, teardown);
|
|
tcase_add_test(tc_conn, hl_getEndpoints);
|
|
tcase_add_test(tc_conn, hl_findServers);
|
|
tcase_add_test(tc_conn, hl_connectDisconnect);
|
|
tcase_add_test(tc_conn, hl_connectFailed);
|
|
|
|
Suite *s = suite_create("Client HighLevel API Extended");
|
|
suite_add_tcase(s, tc_write);
|
|
suite_add_tcase(s, tc_nodes);
|
|
suite_add_tcase(s, tc_browse);
|
|
suite_add_tcase(s, tc_read);
|
|
#ifdef UA_ENABLE_SUBSCRIPTIONS
|
|
suite_add_tcase(s, tc_sub);
|
|
#endif
|
|
suite_add_tcase(s, tc_conn);
|
|
return s;
|
|
}
|
|
|
|
int main(void) {
|
|
int number_failed = 0;
|
|
Suite *s = testSuite_clientHL2();
|
|
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;
|
|
}
|