tests(client): add coverage tests for client sources

This commit is contained in:
Andreas Ebner
2026-03-11 00:23:47 +01:00
committed by GitHub
parent d54cffbe5b
commit bb2b7ef7b7
7 changed files with 1110 additions and 3 deletions
+1
View File
@@ -161,6 +161,7 @@ getRemoteDataTypes(UA_Client *client, UA_ReadRequest *req,
*outCustomTypes = dta;
UA_ReadResponse_clear(&rr);
UA_ReadRequest_clear(req);
if(dta->typesSize == 0) {
UA_free(dta->types);
+323
View File
@@ -451,6 +451,312 @@ START_TEST(Client_closes_on_server_error) {
}
END_TEST
static void timedCallbackFired(UA_Client *c, void *data) {
UA_Boolean *flag = (UA_Boolean *)data;
*flag = true;
}
START_TEST(Client_addTimedCallback) {
UA_Client *client = UA_Client_newForUnitTest();
UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
UA_Boolean fired = false;
UA_UInt64 cbId = 0;
/* Use a deadline in the past so it fires on the next iteration.
* The event loop uses UA_DateTime_now_fake which starts at a low value. */
retval = UA_Client_addTimedCallback(client, timedCallbackFired,
&fired, 1, &cbId);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
ck_assert(cbId != 0);
/* Iterate enough for the timer to fire */
for(int i = 0; i < 20; i++) {
UA_fakeSleep(10);
UA_Client_run_iterate(client, 1);
}
ck_assert(fired == true);
UA_Client_disconnect(client);
UA_Client_delete(client);
}
END_TEST
START_TEST(Client_addRepeatedCallback) {
UA_Client *client = UA_Client_newForUnitTest();
UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
UA_Boolean fired = false;
UA_UInt64 cbId = 0;
retval = UA_Client_addRepeatedCallback(client, timedCallbackFired,
&fired, 50.0, &cbId);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
ck_assert(cbId != 0);
for(int i = 0; i < 20; i++) {
UA_fakeSleep(10);
UA_Client_run_iterate(client, 1);
}
ck_assert(fired == true);
/* Remove the callback */
UA_Client_removeCallback(client, cbId);
UA_Client_disconnect(client);
UA_Client_delete(client);
}
END_TEST
START_TEST(Client_changeRepeatedCallbackInterval) {
UA_Client *client = UA_Client_newForUnitTest();
UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
UA_Boolean fired = false;
UA_UInt64 cbId = 0;
retval = UA_Client_addRepeatedCallback(client, timedCallbackFired,
&fired, 5000.0, &cbId);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
/* Change to a shorter interval */
retval = UA_Client_changeRepeatedCallbackInterval(client, cbId, 20.0);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
for(int i = 0; i < 20; i++) {
UA_fakeSleep(10);
UA_Client_run_iterate(client, 1);
}
ck_assert(fired == true);
UA_Client_removeCallback(client, cbId);
UA_Client_disconnect(client);
UA_Client_delete(client);
}
END_TEST
START_TEST(Client_getConnectionAttribute) {
UA_Client *client = UA_Client_newForUnitTest();
UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
/* Read serverDescription */
UA_Variant val;
UA_QualifiedName key = {0, UA_STRING_STATIC("serverDescription")};
retval = UA_Client_getConnectionAttribute(client, key, &val);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
ck_assert(val.type == &UA_TYPES[UA_TYPES_APPLICATIONDESCRIPTION]);
/* Read securityPolicyUri */
UA_Variant val2;
UA_QualifiedName key2 = {0, UA_STRING_STATIC("securityPolicyUri")};
retval = UA_Client_getConnectionAttribute(client, key2, &val2);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
ck_assert(val2.type == &UA_TYPES[UA_TYPES_STRING]);
/* Read securityMode */
UA_Variant val3;
UA_QualifiedName key3 = {0, UA_STRING_STATIC("securityMode")};
retval = UA_Client_getConnectionAttribute(client, key3, &val3);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
ck_assert(val3.type == &UA_TYPES[UA_TYPES_MESSAGESECURITYMODE]);
/* Read unknown key */
UA_Variant val4;
UA_QualifiedName key4 = {0, UA_STRING_STATIC("unknownKey")};
retval = UA_Client_getConnectionAttribute(client, key4, &val4);
ck_assert_uint_eq(retval, UA_STATUSCODE_BADINTERNALERROR);
UA_Client_disconnect(client);
UA_Client_delete(client);
}
END_TEST
START_TEST(Client_getConnectionAttributeCopy) {
UA_Client *client = UA_Client_newForUnitTest();
UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
UA_Variant val;
UA_QualifiedName key = {0, UA_STRING_STATIC("serverDescription")};
retval = UA_Client_getConnectionAttributeCopy(client, key, &val);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
ck_assert(val.type == &UA_TYPES[UA_TYPES_APPLICATIONDESCRIPTION]);
UA_Variant_clear(&val);
UA_Client_disconnect(client);
UA_Client_delete(client);
}
END_TEST
START_TEST(Client_getConnectionAttribute_scalar) {
UA_Client *client = UA_Client_newForUnitTest();
UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
/* Read securityMode as scalar */
UA_MessageSecurityMode mode;
UA_QualifiedName key = {0, UA_STRING_STATIC("securityMode")};
retval = UA_Client_getConnectionAttribute_scalar(client, key,
&UA_TYPES[UA_TYPES_MESSAGESECURITYMODE], &mode);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
/* Type mismatch path */
UA_Int32 wrongType;
retval = UA_Client_getConnectionAttribute_scalar(client, key,
&UA_TYPES[UA_TYPES_INT32], &wrongType);
ck_assert_uint_eq(retval, UA_STATUSCODE_BADNOTFOUND);
UA_Client_disconnect(client);
UA_Client_delete(client);
}
END_TEST
START_TEST(Client_getNamespaceIndex) {
UA_Client *client = UA_Client_newForUnitTest();
UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
/* Read existing namespace (ns=0 is always "http://opcfoundation.org/UA/") */
UA_ReadRequest rreq;
UA_ReadRequest_init(&rreq);
UA_ReadValueId rvi;
UA_ReadValueId_init(&rvi);
rvi.nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_NAMESPACEARRAY);
rvi.attributeId = UA_ATTRIBUTEID_VALUE;
rreq.nodesToRead = &rvi;
rreq.nodesToReadSize = 1;
UA_ReadResponse rresp = UA_Client_Service_read(client, rreq);
ck_assert_uint_eq(rresp.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
UA_ReadResponse_clear(&rresp);
/* Add and look up a namespace */
UA_String nsUri = UA_STRING("http://test.namespace.example.com/");
UA_UInt16 nsIndex = 0;
retval = UA_Client_addNamespace(client, nsUri, &nsIndex);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
ck_assert(nsIndex > 0);
/* Look up again — should find it */
UA_UInt16 nsIndex2 = 0;
retval = UA_Client_getNamespaceIndex(client, nsUri, &nsIndex2);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
ck_assert_uint_eq(nsIndex, nsIndex2);
/* Add same namespace — should return existing index */
UA_UInt16 nsIndex3 = 0;
retval = UA_Client_addNamespace(client, nsUri, &nsIndex3);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
ck_assert_uint_eq(nsIndex, nsIndex3);
/* Non-existent namespace */
UA_String unknown = UA_STRING("http://nonexistent/");
UA_UInt16 nsIndex4 = 0;
retval = UA_Client_getNamespaceIndex(client, unknown, &nsIndex4);
ck_assert_uint_eq(retval, UA_STATUSCODE_BADNOTFOUND);
UA_Client_disconnect(client);
UA_Client_delete(client);
}
END_TEST
START_TEST(Client_findDataType) {
UA_Client *client = UA_Client_newForUnitTest();
/* Known built-in type */
const UA_DataType *dt =
UA_Client_findDataType(client, &UA_TYPES[UA_TYPES_INT32].typeId);
ck_assert_ptr_ne(dt, NULL);
ck_assert(UA_NodeId_equal(&dt->typeId, &UA_TYPES[UA_TYPES_INT32].typeId));
/* Unknown type */
UA_NodeId unknownId = UA_NODEID_NUMERIC(99, 99999);
dt = UA_Client_findDataType(client, &unknownId);
ck_assert_ptr_eq(dt, NULL);
UA_Client_delete(client);
}
END_TEST
START_TEST(Client_getState) {
UA_Client *client = UA_Client_newForUnitTest();
/* Before connecting */
UA_SecureChannelState channelState;
UA_SessionState sessionState;
UA_StatusCode connectStatus;
UA_Client_getState(client, &channelState, &sessionState, &connectStatus);
ck_assert_uint_eq(channelState, UA_SECURECHANNELSTATE_CLOSED);
ck_assert_uint_eq(sessionState, UA_SESSIONSTATE_CLOSED);
ck_assert_uint_eq(connectStatus, UA_STATUSCODE_GOOD);
/* With NULL outputs */
UA_Client_getState(client, NULL, NULL, NULL);
/* After connecting */
UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
UA_Client_getState(client, &channelState, &sessionState, &connectStatus);
ck_assert_uint_eq(channelState, UA_SECURECHANNELSTATE_OPEN);
ck_assert_uint_eq(sessionState, UA_SESSIONSTATE_ACTIVATED);
ck_assert_uint_eq(connectStatus, UA_STATUSCODE_GOOD);
UA_Client_disconnect(client);
UA_Client_delete(client);
}
END_TEST
START_TEST(Client_cancelByRequestHandle) {
UA_Client *client = UA_Client_newForUnitTest();
UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
/* Cancel a non-existent request handle — server should still return GOOD
* (the CancelResponse itself succeeds, cancelCount is 0) */
UA_UInt32 cancelCount = 99;
retval = UA_Client_cancelByRequestHandle(client, 999999, &cancelCount);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
ck_assert_uint_eq(cancelCount, 0);
UA_Client_disconnect(client);
UA_Client_delete(client);
}
END_TEST
START_TEST(Client_cancelByRequestId_notFound) {
UA_Client *client = UA_Client_newForUnitTest();
UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
/* Request id not in the async list -> BADNOTFOUND */
UA_UInt32 cancelCount = 0;
retval = UA_Client_cancelByRequestId(client, 999999, &cancelCount);
ck_assert_uint_eq(retval, UA_STATUSCODE_BADNOTFOUND);
UA_Client_disconnect(client);
UA_Client_delete(client);
}
END_TEST
START_TEST(Client_setAuthenticationUsername) {
UA_Client *client = UA_Client_newForUnitTest();
UA_ClientConfig *cc = UA_Client_getConfig(client);
UA_StatusCode retval =
UA_ClientConfig_setAuthenticationUsername(cc, "user1", "password");
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
ck_assert(cc->userIdentityToken.content.decoded.type ==
&UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN]);
retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
UA_Client_disconnect(client);
UA_Client_delete(client);
}
END_TEST
static Suite* testSuite_Client(void) {
Suite *s = suite_create("Client");
TCase *tc_client = tcase_create("Client Basic");
@@ -475,6 +781,23 @@ static Suite* testSuite_Client(void) {
tcase_add_test(tc_client_reconnect, Client_activateSessionTimeout);
tcase_add_test(tc_client_reconnect, Client_activateSessionLocaleIds);
suite_add_tcase(s,tc_client_reconnect);
TCase *tc_ext = tcase_create("Client Extended");
tcase_add_checked_fixture(tc_ext, setup, teardown);
tcase_add_test(tc_ext, Client_addTimedCallback);
tcase_add_test(tc_ext, Client_addRepeatedCallback);
tcase_add_test(tc_ext, Client_changeRepeatedCallbackInterval);
tcase_add_test(tc_ext, Client_getConnectionAttribute);
tcase_add_test(tc_ext, Client_getConnectionAttributeCopy);
tcase_add_test(tc_ext, Client_getConnectionAttribute_scalar);
tcase_add_test(tc_ext, Client_getNamespaceIndex);
tcase_add_test(tc_ext, Client_findDataType);
tcase_add_test(tc_ext, Client_getState);
tcase_add_test(tc_ext, Client_cancelByRequestHandle);
tcase_add_test(tc_ext, Client_cancelByRequestId_notFound);
tcase_add_test(tc_ext, Client_setAuthenticationUsername);
suite_add_tcase(s, tc_ext);
return s;
}
+91
View File
@@ -187,6 +187,94 @@ START_TEST(Client_run_iterate) {
}
END_TEST
START_TEST(Client_connectSecureChannelAsync) {
/* Connect only the SecureChannel without opening a Session */
UA_Client *client = UA_Client_newForUnitTest();
UA_StatusCode retval = UA_Client_connectSecureChannelAsync(client,
"opc.tcp://localhost:4840");
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
/* Drive both sides until the channel is open */
while(client->channel.state != UA_SECURECHANNELSTATE_OPEN) {
UA_Server_run_iterate(server, false);
UA_Client_run_iterate(client, 0);
}
UA_SecureChannelState channelState;
UA_SessionState sessionState;
UA_StatusCode connectStatus;
UA_Client_getState(client, &channelState, &sessionState, &connectStatus);
ck_assert_uint_eq(channelState, UA_SECURECHANNELSTATE_OPEN);
/* No session should be created */
ck_assert(sessionState != UA_SESSIONSTATE_ACTIVATED);
UA_Client_disconnectAsync(client);
while(client->channel.state != UA_SECURECHANNELSTATE_CLOSED) {
UA_Server_run_iterate(server, false);
UA_Client_run_iterate(client, 0);
}
UA_Client_delete(client);
}
END_TEST
START_TEST(Client_connect_async_wrongUrl) {
/* Attempt to connect to a wrong port — should fail gracefully */
UA_Client *client = UA_Client_newForUnitTest();
UA_ClientConfig *cc = UA_Client_getConfig(client);
cc->timeout = 200; /* short timeout */
UA_StatusCode retval = UA_Client_connectAsync(client, "opc.tcp://localhost:4841");
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
/* Drive a few iterations */
for(int i = 0; i < 50; i++) {
UA_fakeSleep(10);
UA_Client_run_iterate(client, 0);
}
UA_SecureChannelState channelState;
UA_StatusCode connectStatus;
UA_Client_getState(client, &channelState, NULL, &connectStatus);
/* Either still connecting or failed — not OPEN */
ck_assert(channelState != UA_SECURECHANNELSTATE_OPEN ||
connectStatus != UA_STATUSCODE_GOOD);
UA_Client_delete(client);
}
END_TEST
START_TEST(Client_connectUsername_async) {
/* Connect with username/password via the sync connectUsername wrapper,
* but using manual iteration for the server side */
UA_Client *client = UA_Client_newForUnitTest();
UA_ClientConfig *cc = UA_Client_getConfig(client);
cc->stateCallback = currentState;
connected = false;
UA_StatusCode retval = UA_Client_connectAsync(client, "opc.tcp://localhost:4840");
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
while(!connected) {
UA_Server_run_iterate(server, false);
retval = UA_Client_run_iterate(client, 0);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
}
/* Verify the client is connected with an active session */
UA_SecureChannelState channelState;
UA_SessionState sessionState;
UA_Client_getState(client, &channelState, &sessionState, NULL);
ck_assert_uint_eq(channelState, UA_SECURECHANNELSTATE_OPEN);
ck_assert_uint_eq(sessionState, UA_SESSIONSTATE_ACTIVATED);
/* Disconnect asynchronously */
UA_Client_disconnectAsync(client);
while(client->channel.state != UA_SECURECHANNELSTATE_CLOSED) {
UA_Server_run_iterate(server, false);
UA_Client_run_iterate(client, 0);
}
UA_Client_delete(client);
}
END_TEST
static Suite* testSuite_Client(void) {
Suite *s = suite_create("Client");
TCase *tc_client_connect = tcase_create("Client Connect Async");
@@ -196,6 +284,9 @@ static Suite* testSuite_Client(void) {
tcase_add_test(tc_client_connect, Client_no_connection);
tcase_add_test(tc_client_connect, Client_without_run_iterate);
tcase_add_test(tc_client_connect, Client_run_iterate);
tcase_add_test(tc_client_connect, Client_connectSecureChannelAsync);
tcase_add_test(tc_client_connect, Client_connect_async_wrongUrl);
tcase_add_test(tc_client_connect, Client_connectUsername_async);
suite_add_tcase(s,tc_client_connect);
return s;
}
+89
View File
@@ -60,11 +60,100 @@ START_TEST(Client_connect_badEndpointUrl) {
}
END_TEST
START_TEST(Client_getEndpoints) {
UA_Client *client = UA_Client_newForUnitTest();
size_t endpointCount = 0;
UA_EndpointDescription *endpoints = NULL;
UA_StatusCode retval = UA_Client_getEndpoints(client,
"opc.tcp://localhost:4840",
&endpointCount, &endpoints);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
ck_assert(endpointCount > 0);
ck_assert_ptr_ne(endpoints, NULL);
/* Verify each endpoint has a non-empty securityPolicyUri */
for(size_t i = 0; i < endpointCount; i++) {
ck_assert(endpoints[i].securityPolicyUri.length > 0);
}
UA_Array_delete(endpoints, endpointCount,
&UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]);
UA_Client_delete(client);
}
END_TEST
START_TEST(Client_findServers) {
UA_Client *client = UA_Client_newForUnitTest();
size_t serverCount = 0;
UA_ApplicationDescription *servers = NULL;
UA_StatusCode retval = UA_Client_findServers(client,
"opc.tcp://localhost:4840",
0, NULL, 0, NULL,
&serverCount, &servers);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
ck_assert(serverCount > 0);
ck_assert_ptr_ne(servers, NULL);
UA_Array_delete(servers, serverCount,
&UA_TYPES[UA_TYPES_APPLICATIONDESCRIPTION]);
UA_Client_delete(client);
}
END_TEST
START_TEST(Client_getEndpoints_connected) {
/* Connect first, then getEndpoints on same URL — should succeed */
UA_Client *client = UA_Client_newForUnitTest();
UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
size_t endpointCount = 0;
UA_EndpointDescription *endpoints = NULL;
retval = UA_Client_getEndpoints(client, "opc.tcp://localhost:4840",
&endpointCount, &endpoints);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
ck_assert(endpointCount > 0);
UA_Array_delete(endpoints, endpointCount,
&UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]);
UA_Client_disconnect(client);
UA_Client_delete(client);
}
END_TEST
START_TEST(Client_findServers_connected) {
/* Connect first, then findServers on same URL — should succeed */
UA_Client *client = UA_Client_newForUnitTest();
UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
size_t serverCount = 0;
UA_ApplicationDescription *servers = NULL;
retval = UA_Client_findServers(client, "opc.tcp://localhost:4840",
0, NULL, 0, NULL,
&serverCount, &servers);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
ck_assert(serverCount > 0);
UA_Array_delete(servers, serverCount,
&UA_TYPES[UA_TYPES_APPLICATIONDESCRIPTION]);
UA_Client_disconnect(client);
UA_Client_delete(client);
}
END_TEST
static Suite* testSuite_Client(void) {
Suite *s = suite_create("Client");
TCase *tc_client = tcase_create("Client Discovery");
tcase_add_checked_fixture(tc_client, setup, teardown);
tcase_add_test(tc_client, Client_connect_badEndpointUrl);
tcase_add_test(tc_client, Client_getEndpoints);
tcase_add_test(tc_client, Client_findServers);
tcase_add_test(tc_client, Client_getEndpoints_connected);
tcase_add_test(tc_client, Client_findServers_connected);
suite_add_tcase(s,tc_client);
return s;
}
+199
View File
@@ -1034,6 +1034,188 @@ END_TEST
#endif
/* Extended tests for untested highlevel functions */
START_TEST(Highlevel_TranslateBrowsePath) {
/* Translate a browse path from Objects folder to Server node */
UA_BrowsePath bp;
UA_BrowsePath_init(&bp);
bp.startingNode = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
UA_RelativePathElement rpe;
UA_RelativePathElement_init(&rpe);
rpe.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
rpe.targetName = UA_QUALIFIEDNAME(0, "Server");
bp.relativePath.elementsSize = 1;
bp.relativePath.elements = &rpe;
UA_BrowsePathResult res =
UA_Client_translateBrowsePathToNodeIds(client, &bp);
ck_assert_uint_eq(res.statusCode, UA_STATUSCODE_GOOD);
ck_assert(res.targetsSize > 0);
UA_NodeId serverNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER);
ck_assert(UA_NodeId_equal(&res.targets[0].targetId.nodeId,
&serverNodeId));
UA_BrowsePathResult_clear(&res);
}
END_TEST
START_TEST(Highlevel_WriteValueScalar) {
/* Write a scalar value using writeValueAttribute_scalar and read back */
UA_Int32 newVal = 42;
UA_StatusCode retval =
UA_Client_writeValueAttribute_scalar(client,
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STATE),
&newVal, &UA_TYPES[UA_TYPES_INT32]);
/* Access may be denied because serverstatus/state is read-only,
* but the function path is exercised */
(void)retval;
}
END_TEST
START_TEST(Highlevel_WriteValueEx) {
/* Write a DataValue using writeValueAttributeEx and read back */
UA_DataValue dv;
UA_DataValue_init(&dv);
dv.hasValue = true;
UA_Int32 val = 99;
UA_Variant_setScalar(&dv.value, &val, &UA_TYPES[UA_TYPES_INT32]);
dv.hasSourceTimestamp = true;
dv.sourceTimestamp = UA_DateTime_now();
UA_StatusCode retval =
UA_Client_writeValueAttributeEx(client,
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STATE),
&dv);
/* May be denied on read-only node but exercises the code path */
(void)retval;
}
END_TEST
#ifdef UA_ENABLE_NODEMANAGEMENT
START_TEST(Highlevel_ReadWriteAccessLevelEx) {
/* Create a variable node to test AccessLevelEx */
UA_VariableAttributes attr = UA_VariableAttributes_default;
attr.displayName = UA_LOCALIZEDTEXT("en-US", "ALExTest");
attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
attr.writeMask = 0xFFFFFFFF;
UA_Int32 intVal = 123;
UA_Variant_setScalar(&attr.value, &intVal, &UA_TYPES[UA_TYPES_INT32]);
attr.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
UA_NodeId outId = UA_NODEID_NULL;
UA_StatusCode retval =
UA_Client_addVariableNode(client, UA_NODEID_NULL,
UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
UA_QUALIFIEDNAME(1, "ALExTest"),
UA_NODEID_NULL, attr, &outId);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
/* Read AccessLevelEx */
UA_UInt32 alEx = 0;
retval = UA_Client_readAccessLevelExAttribute(client, outId, &alEx);
/* May not be supported → either GOOD or BADATTRIBUTEIDINVALID */
if(retval == UA_STATUSCODE_GOOD) {
/* Write AccessLevelEx */
UA_UInt32 newAlEx = alEx | 0x100; /* set a higher bit */
retval = UA_Client_writeAccessLevelExAttribute(client, outId, &newAlEx);
/* May fail, but exercises the path */
(void)retval;
}
/* Cleanup */
UA_Client_deleteNode(client, outId, true);
UA_NodeId_clear(&outId);
}
END_TEST
#endif
START_TEST(Highlevel_ForEachChildNode_InvalidNode) {
/* forEachChildNodeCall with non-existent node — exercises the browse path.
* The server may return GOOD with empty results for unknown nodes. */
UA_NodeId invalidNode = UA_NODEID_NUMERIC(99, 99999);
unsigned int prevCount = iteratedNodeCount;
UA_Client_forEachChildNodeCall(client, invalidNode, nodeIter, NULL);
/* No children should have been iterated */
ck_assert_uint_eq(iteratedNodeCount, prevCount);
}
END_TEST
START_TEST(Highlevel_WriteRead_Direct) {
/* UA_Client_write with NULL argument */
UA_StatusCode retval = UA_Client_write(client, NULL);
ck_assert_uint_eq(retval, UA_STATUSCODE_BADINTERNALERROR);
/* UA_Client_write with a valid write value */
UA_WriteValue wv;
UA_WriteValue_init(&wv);
wv.nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME);
wv.attributeId = UA_ATTRIBUTEID_VALUE;
wv.value.hasValue = true;
UA_DateTime now = UA_DateTime_now();
UA_Variant_setScalar(&wv.value.value, &now, &UA_TYPES[UA_TYPES_DATETIME]);
retval = UA_Client_write(client, &wv);
/* Result may be BADWRITENOTSUPPORTED for read-only node, but path is hit */
(void)retval;
/* UA_Client_read with a valid read value id */
UA_ReadValueId rv;
UA_ReadValueId_init(&rv);
rv.nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME);
rv.attributeId = UA_ATTRIBUTEID_VALUE;
UA_DataValue dval = UA_Client_read(client, &rv);
ck_assert_uint_eq(dval.status, UA_STATUSCODE_GOOD);
ck_assert(dval.hasValue);
UA_DataValue_clear(&dval);
}
END_TEST
START_TEST(Highlevel_BrowseSimplified) {
/* Test UA_Client_browse highlevel wrapper */
UA_BrowseDescription bd;
UA_BrowseDescription_init(&bd);
bd.nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER);
bd.resultMask = UA_BROWSERESULTMASK_ALL;
UA_BrowseResult br = UA_Client_browse(client, NULL, 2, &bd);
ck_assert_uint_eq(br.statusCode, UA_STATUSCODE_GOOD);
ck_assert(br.referencesSize > 0);
/* Use browseNext if there's a continuation point */
if(br.continuationPoint.length > 0) {
UA_BrowseResult br2 =
UA_Client_browseNext(client, false, br.continuationPoint);
ck_assert_uint_eq(br2.statusCode, UA_STATUSCODE_GOOD);
/* Release continuation point */
if(br2.continuationPoint.length > 0) {
UA_BrowseResult br3 =
UA_Client_browseNext(client, true, br2.continuationPoint);
UA_BrowseResult_clear(&br3);
}
UA_BrowseResult_clear(&br2);
}
UA_BrowseResult_clear(&br);
}
END_TEST
#ifdef UA_ENABLE_METHODCALLS
START_TEST(Highlevel_CallMethod_Error) {
/* Call a method on a non-existent node */
size_t outputSize = 0;
UA_Variant *output = NULL;
UA_StatusCode retval = UA_Client_call(client,
UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
UA_NODEID_NUMERIC(1, 99999),
0, NULL, &outputSize, &output);
ck_assert(retval != UA_STATUSCODE_GOOD);
ck_assert_uint_eq(outputSize, 0);
ck_assert(output == NULL);
}
END_TEST
#endif
static Suite *testSuite_Client(void) {
Suite *s = suite_create("Client Highlevel");
TCase *tc_misc = tcase_create("Client Highlevel Misc");
@@ -1081,6 +1263,23 @@ static Suite *testSuite_Client(void) {
tcase_add_test(tc_readwrite, Node_ReadWrite_UserExecutable);
suite_add_tcase(s, tc_readwrite);
#endif
TCase *tc_ext = tcase_create("Client Highlevel Extended");
tcase_add_checked_fixture(tc_ext, setup, teardown);
tcase_add_test(tc_ext, Highlevel_TranslateBrowsePath);
tcase_add_test(tc_ext, Highlevel_WriteValueScalar);
tcase_add_test(tc_ext, Highlevel_WriteValueEx);
#ifdef UA_ENABLE_NODEMANAGEMENT
tcase_add_test(tc_ext, Highlevel_ReadWriteAccessLevelEx);
#endif
tcase_add_test(tc_ext, Highlevel_ForEachChildNode_InvalidNode);
tcase_add_test(tc_ext, Highlevel_WriteRead_Direct);
tcase_add_test(tc_ext, Highlevel_BrowseSimplified);
#ifdef UA_ENABLE_METHODCALLS
tcase_add_test(tc_ext, Highlevel_CallMethod_Error);
#endif
suite_add_tcase(s, tc_ext);
return s;
}
+340
View File
@@ -1597,6 +1597,339 @@ START_TEST(Client_subscription_transfer) {
}
END_TEST
START_TEST(Client_subscription_getSetContext) {
UA_Client *client = UA_Client_newForUnitTest();
UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
/* Create subscription */
UA_CreateSubscriptionRequest request = UA_CreateSubscriptionRequest_default();
UA_CreateSubscriptionResponse response =
UA_Client_Subscriptions_create(client, request, NULL, NULL, NULL);
ck_assert_uint_eq(response.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
UA_UInt32 subId = response.subscriptionId;
/* Get context — initially NULL */
void *ctx = (void*)0xDEAD;
retval = UA_Client_Subscriptions_getContext(client, subId, &ctx);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
ck_assert_ptr_eq(ctx, NULL);
/* Set context */
int myData = 42;
retval = UA_Client_Subscriptions_setContext(client, subId, &myData);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
/* Get context again */
retval = UA_Client_Subscriptions_getContext(client, subId, &ctx);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
ck_assert_ptr_eq(ctx, &myData);
/* Invalid subscription id */
retval = UA_Client_Subscriptions_getContext(client, 99999, &ctx);
ck_assert_uint_eq(retval, UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID);
retval = UA_Client_Subscriptions_setContext(client, 99999, &myData);
ck_assert_uint_eq(retval, UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID);
/* NULL args */
retval = UA_Client_Subscriptions_getContext(client, subId, NULL);
ck_assert_uint_eq(retval, UA_STATUSCODE_BADINVALIDARGUMENT);
retval = UA_Client_Subscriptions_getContext(NULL, subId, &ctx);
ck_assert_uint_eq(retval, UA_STATUSCODE_BADINVALIDARGUMENT);
UA_Client_Subscriptions_deleteSingle(client, subId);
UA_Client_disconnect(client);
UA_Client_delete(client);
}
END_TEST
START_TEST(Client_subscription_deleteSingle) {
UA_Client *client = UA_Client_newForUnitTest();
UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
/* Create subscription */
UA_CreateSubscriptionRequest request = UA_CreateSubscriptionRequest_default();
UA_CreateSubscriptionResponse response =
UA_Client_Subscriptions_create(client, request, NULL, NULL, NULL);
ck_assert_uint_eq(response.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
UA_UInt32 subId = response.subscriptionId;
/* Delete it */
retval = UA_Client_Subscriptions_deleteSingle(client, subId);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
/* Delete again — should fail */
retval = UA_Client_Subscriptions_deleteSingle(client, subId);
ck_assert(retval != UA_STATUSCODE_GOOD);
UA_Client_disconnect(client);
UA_Client_delete(client);
}
END_TEST
START_TEST(Client_subscription_setPublishingMode) {
UA_Client *client = UA_Client_newForUnitTest();
UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
/* Create subscription */
UA_CreateSubscriptionRequest request = UA_CreateSubscriptionRequest_default();
UA_CreateSubscriptionResponse response =
UA_Client_Subscriptions_create(client, request, NULL, NULL, NULL);
ck_assert_uint_eq(response.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
UA_UInt32 subId = response.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);
ck_assert_uint_eq(spmResp.resultsSize, 1);
ck_assert_uint_eq(spmResp.results[0], UA_STATUSCODE_GOOD);
UA_SetPublishingModeResponse_clear(&spmResp);
/* Re-enable publishing */
spmReq.publishingEnabled = true;
spmResp = UA_Client_Subscriptions_setPublishingMode(client, spmReq);
ck_assert_uint_eq(spmResp.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
UA_SetPublishingModeResponse_clear(&spmResp);
UA_Client_Subscriptions_deleteSingle(client, subId);
UA_Client_disconnect(client);
UA_Client_delete(client);
}
END_TEST
static void
dataChangeCallback_ext(UA_Client *c, UA_UInt32 subId, void *subContext,
UA_UInt32 monId, void *monContext,
UA_DataValue *value) {
/* no-op: just need a valid callback */
}
START_TEST(Client_monitoredItem_getSetContext) {
UA_Client *client = UA_Client_newForUnitTest();
UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
/* Create subscription */
UA_CreateSubscriptionRequest request = UA_CreateSubscriptionRequest_default();
UA_CreateSubscriptionResponse response =
UA_Client_Subscriptions_create(client, request, NULL, NULL, NULL);
ck_assert_uint_eq(response.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
UA_UInt32 subId = response.subscriptionId;
/* Create a monitored item */
UA_MonitoredItemCreateRequest monReq =
UA_MonitoredItemCreateRequest_default(
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME));
UA_MonitoredItemCreateResult monResult =
UA_Client_MonitoredItems_createDataChange(client, subId,
UA_TIMESTAMPSTORETURN_BOTH, monReq, NULL, dataChangeCallback_ext, NULL);
ck_assert_uint_eq(monResult.statusCode, UA_STATUSCODE_GOOD);
UA_UInt32 monId = monResult.monitoredItemId;
/* Get context — initially NULL */
void *ctx = (void*)0xDEAD;
retval = UA_Client_MonitoredItem_getContext(client, subId, monId, &ctx);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
ck_assert_ptr_eq(ctx, NULL);
/* Set context */
int myData = 123;
retval = UA_Client_MonitoredItem_setContext(client, subId, monId, &myData);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
/* Get context again */
retval = UA_Client_MonitoredItem_getContext(client, subId, monId, &ctx);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
ck_assert_ptr_eq(ctx, &myData);
/* Invalid subscription id */
retval = UA_Client_MonitoredItem_getContext(client, 99999, monId, &ctx);
ck_assert_uint_eq(retval, UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID);
/* Invalid monitored item id */
retval = UA_Client_MonitoredItem_getContext(client, subId, 99999, &ctx);
ck_assert_uint_eq(retval, UA_STATUSCODE_BADMONITOREDITEMIDINVALID);
/* NULL args */
retval = UA_Client_MonitoredItem_getContext(client, subId, monId, NULL);
ck_assert_uint_eq(retval, UA_STATUSCODE_BADINVALIDARGUMENT);
retval = UA_Client_MonitoredItem_getContext(NULL, subId, monId, &ctx);
ck_assert_uint_eq(retval, UA_STATUSCODE_BADINVALIDARGUMENT);
/* Set context with invalid sub */
retval = UA_Client_MonitoredItem_setContext(client, 99999, monId, &myData);
ck_assert_uint_eq(retval, UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID);
retval = UA_Client_MonitoredItem_setContext(client, subId, 99999, &myData);
ck_assert_uint_eq(retval, UA_STATUSCODE_BADMONITOREDITEMIDINVALID);
retval = UA_Client_MonitoredItem_setContext(NULL, subId, monId, &myData);
ck_assert_uint_eq(retval, UA_STATUSCODE_BADINVALIDARGUMENT);
UA_Client_MonitoredItems_deleteSingle(client, subId, monId);
UA_Client_Subscriptions_deleteSingle(client, subId);
UA_Client_disconnect(client);
UA_Client_delete(client);
}
END_TEST
START_TEST(Client_subscription_setMonitoringMode) {
UA_Client *client = UA_Client_newForUnitTest();
UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
/* Create subscription + monitored item */
UA_CreateSubscriptionRequest request = UA_CreateSubscriptionRequest_default();
UA_CreateSubscriptionResponse response =
UA_Client_Subscriptions_create(client, request, NULL, NULL, NULL);
ck_assert_uint_eq(response.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
UA_UInt32 subId = response.subscriptionId;
UA_MonitoredItemCreateRequest monReq =
UA_MonitoredItemCreateRequest_default(
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME));
UA_MonitoredItemCreateResult monResult =
UA_Client_MonitoredItems_createDataChange(client, subId,
UA_TIMESTAMPSTORETURN_BOTH, monReq, NULL, dataChangeCallback_ext, NULL);
ck_assert_uint_eq(monResult.statusCode, UA_STATUSCODE_GOOD);
UA_UInt32 monId = monResult.monitoredItemId;
/* Set monitoring mode to Disabled */
UA_SetMonitoringModeRequest smmReq;
UA_SetMonitoringModeRequest_init(&smmReq);
smmReq.subscriptionId = subId;
smmReq.monitoringMode = UA_MONITORINGMODE_DISABLED;
smmReq.monitoredItemIds = &monId;
smmReq.monitoredItemIdsSize = 1;
UA_SetMonitoringModeResponse smmResp =
UA_Client_MonitoredItems_setMonitoringMode(client, smmReq);
ck_assert_uint_eq(smmResp.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
UA_SetMonitoringModeResponse_clear(&smmResp);
/* Set back to Reporting */
smmReq.monitoringMode = UA_MONITORINGMODE_REPORTING;
smmResp = UA_Client_MonitoredItems_setMonitoringMode(client, smmReq);
ck_assert_uint_eq(smmResp.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
UA_SetMonitoringModeResponse_clear(&smmResp);
UA_Client_MonitoredItems_deleteSingle(client, subId, monId);
UA_Client_Subscriptions_deleteSingle(client, subId);
UA_Client_disconnect(client);
UA_Client_delete(client);
}
END_TEST
START_TEST(Client_subscription_setTriggering) {
UA_Client *client = UA_Client_newForUnitTest();
UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
/* Create subscription */
UA_CreateSubscriptionRequest request = UA_CreateSubscriptionRequest_default();
UA_CreateSubscriptionResponse response =
UA_Client_Subscriptions_create(client, request, NULL, NULL, NULL);
ck_assert_uint_eq(response.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
UA_UInt32 subId = response.subscriptionId;
/* Create trigger and triggering items */
UA_MonitoredItemCreateRequest monReqs[2];
monReqs[0] = UA_MonitoredItemCreateRequest_default(
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME));
monReqs[1] = UA_MonitoredItemCreateRequest_default(
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STATE));
UA_MonitoredItemCreateResult monResults[2];
monResults[0] = UA_Client_MonitoredItems_createDataChange(client, subId,
UA_TIMESTAMPSTORETURN_BOTH, monReqs[0], NULL, dataChangeCallback_ext, NULL);
ck_assert_uint_eq(monResults[0].statusCode, UA_STATUSCODE_GOOD);
monResults[1] = UA_Client_MonitoredItems_createDataChange(client, subId,
UA_TIMESTAMPSTORETURN_BOTH, monReqs[1], NULL, dataChangeCallback_ext, NULL);
ck_assert_uint_eq(monResults[1].statusCode, UA_STATUSCODE_GOOD);
UA_UInt32 triggeringItemId = monResults[0].monitoredItemId;
UA_UInt32 linkedItemId = monResults[1].monitoredItemId;
/* SetTriggering: add a link */
UA_SetTriggeringRequest stReq;
UA_SetTriggeringRequest_init(&stReq);
stReq.subscriptionId = subId;
stReq.triggeringItemId = triggeringItemId;
stReq.linksToAdd = &linkedItemId;
stReq.linksToAddSize = 1;
UA_SetTriggeringResponse stResp =
UA_Client_MonitoredItems_setTriggering(client, stReq);
ck_assert_uint_eq(stResp.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
UA_SetTriggeringResponse_clear(&stResp);
/* Remove the link */
UA_SetTriggeringRequest stReq2;
UA_SetTriggeringRequest_init(&stReq2);
stReq2.subscriptionId = subId;
stReq2.triggeringItemId = triggeringItemId;
stReq2.linksToRemove = &linkedItemId;
stReq2.linksToRemoveSize = 1;
stResp = UA_Client_MonitoredItems_setTriggering(client, stReq2);
ck_assert_uint_eq(stResp.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
UA_SetTriggeringResponse_clear(&stResp);
UA_Client_MonitoredItems_deleteSingle(client, subId, monResults[0].monitoredItemId);
UA_Client_MonitoredItems_deleteSingle(client, subId, monResults[1].monitoredItemId);
UA_Client_Subscriptions_deleteSingle(client, subId);
UA_Client_disconnect(client);
UA_Client_delete(client);
}
END_TEST
START_TEST(Client_subscription_modifyAsync) {
UA_Client *client = UA_Client_newForUnitTest();
UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
/* Create subscription */
UA_CreateSubscriptionRequest request = UA_CreateSubscriptionRequest_default();
UA_CreateSubscriptionResponse response =
UA_Client_Subscriptions_create(client, request, NULL, NULL, NULL);
ck_assert_uint_eq(response.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
UA_UInt32 subId = response.subscriptionId;
/* Modify via async */
UA_ModifySubscriptionRequest modReq;
UA_ModifySubscriptionRequest_init(&modReq);
modReq.subscriptionId = subId;
modReq.requestedPublishingInterval = 200.0;
modReq.requestedMaxKeepAliveCount = 20;
modReq.requestedLifetimeCount = 600;
modReq.maxNotificationsPerPublish = 100;
UA_UInt32 reqId = 0;
retval = UA_Client_Subscriptions_modify_async(client, modReq,
NULL, NULL, &reqId);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
ck_assert(reqId != 0);
/* Drive iterations to get response */
for(int i = 0; i < 20; i++) {
UA_fakeSleep(10);
UA_Client_run_iterate(client, 1);
}
UA_Client_Subscriptions_deleteSingle(client, subId);
UA_Client_disconnect(client);
UA_Client_delete(client);
}
END_TEST
#ifdef UA_ENABLE_METHODCALLS
START_TEST(Client_methodcall) {
UA_Client *client = UA_Client_newForUnitTest();
@@ -1683,6 +2016,13 @@ static Suite* testSuite_Client(void) {
tcase_add_test(tc_client, Client_subscription_server_disappears);
tcase_add_test(tc_client, Client_subscription_transfer);
tcase_add_test(tc_client, Client_subscription_writeBurst);
tcase_add_test(tc_client, Client_subscription_getSetContext);
tcase_add_test(tc_client, Client_subscription_deleteSingle);
tcase_add_test(tc_client, Client_subscription_setPublishingMode);
tcase_add_test(tc_client, Client_monitoredItem_getSetContext);
tcase_add_test(tc_client, Client_subscription_setMonitoringMode);
tcase_add_test(tc_client, Client_subscription_setTriggering);
tcase_add_test(tc_client, Client_subscription_modifyAsync);
suite_add_tcase(s,tc_client);
#ifdef UA_ENABLE_METHODCALLS
@@ -269,9 +269,6 @@ START_TEST(sub_data_change_notification) {
break;
}
/* The subscription create and monitored item create are the main coverage
* hits. Whether the notification arrives depends on timing. */
/* Write a new value */
UA_Int32 newVal = 777;
UA_Variant wv;
@@ -561,6 +558,64 @@ START_TEST(client_register_nodes) {
disconnectClient(client);
} END_TEST
/* === Client getRemoteDataTypes (covers ua_client_util.c) === */
START_TEST(client_getRemoteDataTypes_browse) {
/* Test with dataTypesNodesSize == 0, which triggers the browse-based
* discovery of unknown DataTypes in the server's type hierarchy */
UA_Client *client = connectClient();
UA_DataTypeArray *customTypes = NULL;
UA_StatusCode retval =
UA_Client_getRemoteDataTypes(client, 0, NULL, &customTypes);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
/* Add to client config so it gets cleaned up on client delete */
if(customTypes) {
UA_ClientConfig *cc = UA_Client_getConfig(client);
customTypes->next = cc->customDataTypes;
cc->customDataTypes = customTypes;
}
disconnectClient(client);
}
END_TEST
START_TEST(client_getRemoteDataTypes_explicit) {
/* Test with explicit NodeIds — use a known DataType node e.g. Range */
UA_Client *client = connectClient();
UA_NodeId nodes[1];
nodes[0] = UA_NODEID_NUMERIC(0, UA_NS0ID_RANGE);
UA_DataTypeArray *customTypes = NULL;
UA_StatusCode retval =
UA_Client_getRemoteDataTypes(client, 1, nodes, &customTypes);
/* Range is already known, so the result array may be empty but call succeeds */
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
if(customTypes) {
UA_ClientConfig *cc = UA_Client_getConfig(client);
customTypes->next = cc->customDataTypes;
cc->customDataTypes = customTypes;
}
disconnectClient(client);
}
END_TEST
START_TEST(client_getRemoteDataTypes_nullArg) {
/* NULL customTypes argument => BADINVALIDARGUMENT */
UA_Client *client = connectClient();
UA_StatusCode retval =
UA_Client_getRemoteDataTypes(client, 0, NULL, NULL);
ck_assert_uint_eq(retval, UA_STATUSCODE_BADINVALIDARGUMENT);
disconnectClient(client);
}
END_TEST
/* === Suite definition === */
static Suite *testSuite_clientSubExt2(void) {
TCase *tc_sub = tcase_create("SubLifecycle");
@@ -589,6 +644,15 @@ static Suite *testSuite_clientSubExt2(void) {
suite_add_tcase(s, tc_sub);
suite_add_tcase(s, tc_disc);
suite_add_tcase(s, tc_ops);
TCase *tc_util = tcase_create("ClientUtil");
tcase_add_checked_fixture(tc_util, setup, teardown);
tcase_set_timeout(tc_util, 30);
tcase_add_test(tc_util, client_getRemoteDataTypes_browse);
tcase_add_test(tc_util, client_getRemoteDataTypes_explicit);
tcase_add_test(tc_util, client_getRemoteDataTypes_nullArg);
suite_add_tcase(s, tc_util);
return s;
}