Bladeren bron

LDS customizable timeout for automatic de-register (#695)

* Enable valgrind for unit tests and fix memory leaks

* Fix build errors introduced by cmake rewrite in current master

* LDS customizable timeout for automatic de-register
Stefan Profanter 8 jaren geleden
bovenliggende
commit
658d85d451

+ 2 - 1
examples/CMakeLists.txt

@@ -7,7 +7,7 @@ else()
     add_definitions(-DUA_NO_AMALGAMATION)
 endif()
 
-list(APPEND LIBS ${open62541_LIBRARIES})
+list(APPEND LIBS ${open62541_LIBRARIES} open62541)
 if(NOT WIN32)
   list(APPEND LIBS pthread)
   if (NOT APPLE)
@@ -68,6 +68,7 @@ if(UA_ENABLE_METHODCALLS)
 endif()
 
 if(UA_ENABLE_DISCOVERY)
+
     add_executable(discovery_server_discovery discovery/server_discovery.c)
     target_link_libraries(discovery_server_discovery ${LIBS})
 

+ 7 - 1
examples/discovery/server_discovery.c

@@ -30,7 +30,13 @@ int main(void) {
     UA_ServerConfig config = UA_ServerConfig_standard;
     config.applicationDescription.applicationType = UA_APPLICATIONTYPE_DISCOVERYSERVER;
     config.applicationDescription.applicationUri = UA_String_fromChars("open62541.example.local_discovery_server");
-    UA_ServerNetworkLayer nl = UA_ServerNetworkLayerTCP(UA_ConnectionConfig_standard, 4048);
+    // timeout in seconds when to automatically remove a registered server from the list,
+    // if it doesn't re-register within the given time frame. A value of 0 disables automatic removal.
+    // Default is 60 Minutes (60*60). Must be bigger than 10 seconds, because cleanup is only triggered approximately
+    // ervery 10 seconds.
+    // The server will still be removed depending on the state of the semaphore file.
+    // config.discoveryCleanupTimeout = 60*60;
+    UA_ServerNetworkLayer nl = UA_ServerNetworkLayerTCP(UA_ConnectionConfig_standard, 4840);
     config.networkLayers = &nl;
     config.networkLayersSize = 1;
     UA_Server *server = UA_Server_new(config);

+ 6 - 0
examples/discovery/server_register.c

@@ -173,6 +173,12 @@ int main(int argc, char** argv) {
 
 
     UA_StatusCode retval = UA_Server_run(server, &running);
+    if (retval != UA_STATUSCODE_GOOD) {
+        UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER, "Could not start discovery server. StatusCode 0x%08x", retval);
+        UA_Server_delete(server);
+        nl.deleteMembers(&nl);
+        return (int)retval;
+    }
 
     // UNregister the server from the discovery server.
     retval = UA_Server_unregister_discovery(server, "opc.tcp://localhost:4840" );

+ 11 - 0
include/ua_server.h

@@ -129,6 +129,17 @@ typedef struct {
     /* Limits for MonitoredItems */
     UA_DoubleRange samplingIntervalLimits;
     UA_UInt32Range queueSizeLimits;
+
+#ifdef UA_ENABLE_DISCOVERY
+    /* Discovery */
+    // timeout in seconds when to automatically remove a registered server from the list,
+    // if it doesn't re-register within the given time frame. A value of 0 disables automatic removal.
+    // Default is 60 Minutes (60*60). Must be bigger than 10 seconds, because cleanup is only triggered approximately
+    // ervery 10 seconds.
+    // The server will still be removed depending on the state of the semaphore file.
+    UA_UInt32 discoveryCleanupTimeout;
+#endif
+
 } UA_ServerConfig;
 
 /**

+ 4 - 0
plugins/ua_config_standard.c

@@ -69,6 +69,10 @@ const UA_ServerConfig UA_ServerConfig_standard = {
     /* Limits for MonitoredItems */
     .samplingIntervalLimits = { .min = 50.0, .max = 24.0 * 3600.0 * 1000.0 },
     .queueSizeLimits = { .max = 100, .min = 1 }
+
+#ifdef UA_ENABLE_DISCOVERY
+	,.discoveryCleanupTimeout = 60*60
+#endif
 };
 
 const UA_EXPORT UA_ClientConfig UA_ClientConfig_standard = {

+ 11 - 6
src/server/ua_services_discovery.c

@@ -362,8 +362,10 @@ void Service_RegisterServer(UA_Server *server, UA_Session *session,
 void UA_Discovery_cleanupTimedOut(UA_Server *server, UA_DateTime nowMonotonic) {
 
     UA_DateTime timedOut = nowMonotonic;
-    // registration is timed out if lastSeen is older than 60 minutes.
-    timedOut -= 60*60*UA_SEC_TO_DATETIME;
+    // registration is timed out if lastSeen is older than 60 minutes (default value, can be modified by user).
+    if (server->config.discoveryCleanupTimeout) {
+        timedOut -= server->config.discoveryCleanupTimeout*UA_SEC_TO_DATETIME;
+    }
 
     registeredServer_list_entry* current, *temp;
     LIST_FOREACH_SAFE(current, &server->registeredServers, pointers, temp) {
@@ -378,17 +380,20 @@ void UA_Discovery_cleanupTimedOut(UA_Server *server, UA_DateTime nowMonotonic) {
             free(filePath);
         }
 
-
-        if (semaphoreDeleted || current->lastSeen < timedOut) {
+        if (semaphoreDeleted || (server->config.discoveryCleanupTimeout && current->lastSeen < timedOut)) {
             if (semaphoreDeleted) {
                 UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_SERVER,
                             "Registration of server with URI %.*s is removed because the semaphore file '%.*s' was deleted.",
                             (int)current->registeredServer.serverUri.length, current->registeredServer.serverUri.data,
                             (int)current->registeredServer.semaphoreFilePath.length, current->registeredServer.semaphoreFilePath.data);
             } else {
+                // cppcheck-suppress unreadVariable
+                UA_String lastStr = UA_DateTime_toString(current->lastSeen);
                 UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_SERVER,
-                             "Registration of server with URI %.*s has timed out and is removed",
-                            (int)current->registeredServer.serverUri.length, current->registeredServer.serverUri.data);
+                             "Registration of server with URI %.*s has timed out and is removed. Last seen: %.*s",
+                            (int)current->registeredServer.serverUri.length, current->registeredServer.serverUri.data,
+                            (int)lastStr.length, lastStr.data);
+                UA_free(lastStr.data);
             }
             LIST_REMOVE(current, pointers);
             UA_RegisteredServer_deleteMembers(&current->registeredServer);

+ 30 - 16
tests/CMakeLists.txt

@@ -7,7 +7,7 @@ include_directories(${PROJECT_BINARY_DIR}/src_generated)
 find_package(Check REQUIRED)
 find_package(Threads REQUIRED)
 
-set(LIBS ${CHECK_LIBRARIES} ${open62541_LIBRARIES})
+set(LIBS ${CHECK_LIBRARIES} ${open62541_LIBRARIES} open62541)
 if(NOT WIN32)
   list(APPEND LIBS pthread m)
   if (NOT APPLE)
@@ -24,48 +24,62 @@ if(CMAKE_COMPILER_IS_GNUCC OR "x${CMAKE_C_COMPILER_ID}" STREQUAL "xClang")
     add_definitions(-Wno-sign-conversion)
 endif()
 
+# Valgrind definition
+set(UA_TEST_WITH_VALGRIND ON)
+SET(VALGRIND_FLAGS --quiet --trace-children=yes --leak-check=full)
+macro(add_test_valgrind TEST_NAME)
+    IF(UA_TEST_WITH_VALGRIND)
+        add_test(${TEST_NAME}
+                valgrind --error-exitcode=1 ${VALGRIND_FLAGS} ${ARGN} )
+    ELSE()
+        add_test(${TEST_NAME} ${ARGN})
+    ENDIF()
+endmacro()
+
+
+
 # the unit test are built directly on the open62541 object files. so they can
 # access symbols that are hidden/not exported to the shared library
 
 add_executable(check_builtin check_builtin.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(check_builtin ${LIBS})
-add_test(builtin ${CMAKE_CURRENT_BINARY_DIR}/check_builtin)
+add_test_valgrind(builtin ${CMAKE_CURRENT_BINARY_DIR}/check_builtin)
 
 add_executable(check_memory check_memory.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(check_memory ${LIBS})
-add_test(memory ${CMAKE_CURRENT_BINARY_DIR}/check_memory)
+add_test_valgrind(memory ${CMAKE_CURRENT_BINARY_DIR}/check_memory)
 
 add_executable(check_chunking check_chunking.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(check_chunking ${LIBS})
-add_test(chunking ${CMAKE_CURRENT_BINARY_DIR}/check_chunking)
+add_test_valgrind(chunking ${CMAKE_CURRENT_BINARY_DIR}/check_chunking)
 
 add_executable(check_services_view check_services_view.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(check_services_view ${LIBS})
-add_test(services_view ${CMAKE_CURRENT_BINARY_DIR}/check_services_view)
+add_test_valgrind(services_view ${CMAKE_CURRENT_BINARY_DIR}/check_services_view)
 
 add_executable(check_services_attributes check_services_attributes.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(check_services_attributes ${LIBS})
-add_test(services_attributes ${CMAKE_CURRENT_BINARY_DIR}/check_services_attributes)
+add_test_valgrind(services_attributes ${CMAKE_CURRENT_BINARY_DIR}/check_services_attributes)
 
 add_executable(check_services_nodemanagement check_services_nodemanagement.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(check_services_nodemanagement ${LIBS})
-add_test(services_nodemanagement ${CMAKE_CURRENT_BINARY_DIR}/check_services_nodemanagement)
+add_test_valgrind(services_nodemanagement ${CMAKE_CURRENT_BINARY_DIR}/check_services_nodemanagement)
 
 add_executable(check_nodestore check_nodestore.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(check_nodestore ${LIBS})
-add_test(nodestore ${CMAKE_CURRENT_BINARY_DIR}/check_nodestore)
+add_test_valgrind(nodestore ${CMAKE_CURRENT_BINARY_DIR}/check_nodestore)
 
 add_executable(check_session check_session.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(check_session ${LIBS})
-add_test(session ${CMAKE_CURRENT_BINARY_DIR}/check_session)
+add_test_valgrind(session ${CMAKE_CURRENT_BINARY_DIR}/check_session)
 
 add_executable(check_server_userspace check_server_userspace.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(check_server_userspace ${LIBS})
-add_test(check_server_userspace ${CMAKE_CURRENT_BINARY_DIR}/check_server_userspace)
+add_test_valgrind(check_server_userspace ${CMAKE_CURRENT_BINARY_DIR}/check_server_userspace)
 
 add_executable(check_discovery check_discovery.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(check_discovery ${LIBS})
-add_test(discovery ${CMAKE_CURRENT_BINARY_DIR}/check_discovery)
+add_test_valgrind(discovery ${CMAKE_CURRENT_BINARY_DIR}/check_discovery)
 
 # test with canned interactions from files
 
@@ -98,19 +112,19 @@ target_include_directories(check_server_binary_messages PRIVATE ${PROJECT_SOURCE
 target_link_libraries(check_server_binary_messages ${LIBS})
 add_dependencies(check_server_binary_messages client_HELOPN.bin)
 
-add_test(check_server_binary_messages_browse ${CMAKE_CURRENT_BINARY_DIR}/check_server_binary_messages
+add_test_valgrind(check_server_binary_messages_browse ${CMAKE_CURRENT_BINARY_DIR}/check_server_binary_messages
                                              ${CMAKE_CURRENT_BINARY_DIR}/client_HELOPN.bin
                                              ${CMAKE_CURRENT_BINARY_DIR}/client_CreateActivateSession.bin
                                              ${CMAKE_CURRENT_BINARY_DIR}/client_Browse.bin
                                              ${CMAKE_CURRENT_BINARY_DIR}/client_CLO.bin)
 
-add_test(check_server_binary_messages_read ${CMAKE_CURRENT_BINARY_DIR}/check_server_binary_messages
+add_test_valgrind(check_server_binary_messages_read ${CMAKE_CURRENT_BINARY_DIR}/check_server_binary_messages
                                            ${CMAKE_CURRENT_BINARY_DIR}/client_HELOPN.bin
                                            ${CMAKE_CURRENT_BINARY_DIR}/client_CreateActivateSession.bin
                                            ${CMAKE_CURRENT_BINARY_DIR}/client_Read.bin
                                            ${CMAKE_CURRENT_BINARY_DIR}/client_CLO.bin)
 
-add_test(check_server_binary_messages_write ${CMAKE_CURRENT_BINARY_DIR}/check_server_binary_messages
+add_test_valgrind(check_server_binary_messages_write ${CMAKE_CURRENT_BINARY_DIR}/check_server_binary_messages
                                            ${CMAKE_CURRENT_BINARY_DIR}/client_HELOPN.bin
                                            ${CMAKE_CURRENT_BINARY_DIR}/client_CreateActivateSession.bin
                                            ${CMAKE_CURRENT_BINARY_DIR}/client_Write.bin
@@ -118,8 +132,8 @@ add_test(check_server_binary_messages_write ${CMAKE_CURRENT_BINARY_DIR}/check_se
 
 add_executable(check_client check_client.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(check_client ${LIBS})
-add_test(check_client ${CMAKE_CURRENT_BINARY_DIR}/check_client)
+add_test_valgrind(check_client ${CMAKE_CURRENT_BINARY_DIR}/check_client)
 
 add_executable(check_client_subscriptions check_client_subscriptions.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(check_client_subscriptions ${LIBS})
-add_test(check_client_subscriptions ${CMAKE_CURRENT_BINARY_DIR}/check_client_subscriptions)
+add_test_valgrind(check_client_subscriptions ${CMAKE_CURRENT_BINARY_DIR}/check_client_subscriptions)

+ 76 - 77
tests/check_discovery.c

@@ -3,14 +3,19 @@
 #include <pthread.h>
 #include <ua_util.h>
 #include <ua_types_generated.h>
+#include <server/ua_server_internal.h>
+#include <unistd.h>
 
-#include "ua_server.h"
 #include "ua_client.h"
 #include "ua_config_standard.h"
 #include "ua_network_tcp.h"
 #include "check.h"
 
 
+// set register timeout to 1 second so we are able to test it.
+#define registerTimeout 1
+// cleanup is only triggered every 10 seconds, thus wait a bit longer to check
+#define checkWait registerTimeout + 11
 
 UA_Server *server_lds;
 UA_Boolean *running_lds;
@@ -30,6 +35,7 @@ static void setup_lds(void) {
 	UA_ServerConfig config_lds = UA_ServerConfig_standard;
 	config_lds.applicationDescription.applicationType = UA_APPLICATIONTYPE_DISCOVERYSERVER;
 	config_lds.applicationDescription.applicationUri = UA_String_fromChars("open62541.test.local_discovery_server");
+	config_lds.discoveryCleanupTimeout = registerTimeout;
 	nl_lds = UA_ServerNetworkLayerTCP(UA_ConnectionConfig_standard, 4840);
 	config_lds.networkLayers = &nl_lds;
 	config_lds.networkLayersSize = 1;
@@ -43,6 +49,7 @@ static void teardown_lds(void) {
 	pthread_join(server_thread_lds, NULL);
 	UA_Server_run_shutdown(server_lds);
 	UA_Boolean_delete(running_lds);
+	UA_String_deleteMembers(&server_lds->config.applicationDescription.applicationUri);
 	UA_Server_delete(server_lds);
 	nl_lds.deleteMembers(&nl_lds);
 }
@@ -78,6 +85,7 @@ static void teardown_register(void) {
 	pthread_join(server_thread_register, NULL);
 	UA_Server_run_shutdown(server_register);
 	UA_Boolean_delete(running_register);
+	UA_String_deleteMembers(&server_register->config.applicationDescription.applicationUri);
 	UA_Server_delete(server_register);
 	nl_register.deleteMembers(&nl_register);
 }
@@ -95,7 +103,7 @@ START_TEST(Server_unregister) {
 END_TEST
 
 
-static UA_StatusCode FindServers(const char* discoveryServerUrl, size_t* registeredServerSize, UA_ApplicationDescription** registeredServers, const char* filterUri) {
+static UA_StatusCode FindServers(const char* discoveryServerUrl, size_t* registeredServerSize, UA_ApplicationDescription** registeredServers, const char* filterUri, const char* filterLocale) {
 	UA_Client *client = UA_Client_new(UA_ClientConfig_standard);
 	UA_StatusCode retval = UA_Client_connect(client, discoveryServerUrl);
 	if(retval != UA_STATUSCODE_GOOD) {
@@ -113,6 +121,12 @@ static UA_StatusCode FindServers(const char* discoveryServerUrl, size_t* registe
 		request.serverUris[0] = UA_String_fromChars(filterUri);
 	}
 
+	if (filterLocale) {
+		request.localeIdsSize = 1;
+		request.localeIds = UA_malloc(sizeof(UA_String));
+		request.localeIds[0] = UA_String_fromChars(filterLocale);
+	}
+
 	// now send the request
 	UA_FindServersResponse response;
 	UA_FindServersResponse_init(&response);
@@ -120,7 +134,11 @@ static UA_StatusCode FindServers(const char* discoveryServerUrl, size_t* registe
 						&response, &UA_TYPES[UA_TYPES_FINDSERVERSRESPONSE]);
 
 	if (filterUri) {
-		UA_free(request.serverUris);
+		UA_Array_delete(request.serverUris, request.serverUrisSize, &UA_TYPES[UA_TYPES_STRING]);
+	}
+
+	if (filterLocale) {
+		UA_Array_delete(request.localeIds, request.localeIdsSize, &UA_TYPES[UA_TYPES_STRING]);
 	}
 
 	if(response.responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
@@ -141,100 +159,67 @@ static UA_StatusCode FindServers(const char* discoveryServerUrl, size_t* registe
 	return (int) UA_STATUSCODE_GOOD;
 }
 
-// Test if discovery server lists himself as registered server, before any other registration.
-START_TEST(Client_find_discovery) {
-		UA_Client *client = UA_Client_new(UA_ClientConfig_standard);
-		UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
-
-		ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+static void FindAndCheck(const char* expectedUris[], size_t expectedUrisSize, const char *filterUri, const char *filterLocale) {
+	UA_Client *client = UA_Client_new(UA_ClientConfig_standard);
+	UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
 
-		UA_ApplicationDescription* applicationDescriptionArray = NULL;
-		size_t applicationDescriptionArraySize = 0;
+	ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
 
-		retval = FindServers("opc.tcp://localhost:4840", &applicationDescriptionArraySize, &applicationDescriptionArray, NULL);
-		ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+	UA_ApplicationDescription* applicationDescriptionArray = NULL;
+	size_t applicationDescriptionArraySize = 0;
 
-		// only the discovery server is expected
-		ck_assert_uint_eq(applicationDescriptionArraySize , 1);
+	retval = FindServers("opc.tcp://localhost:4840", &applicationDescriptionArraySize, &applicationDescriptionArray, filterUri, filterLocale);
+	ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
 
+	// only the discovery server is expected
+	ck_assert_uint_eq(applicationDescriptionArraySize , expectedUrisSize);
 
-		char* serverUri = malloc(sizeof(char)*applicationDescriptionArray[0].applicationUri.length+1);
-		memcpy( serverUri, applicationDescriptionArray[0].applicationUri.data, applicationDescriptionArray[0].applicationUri.length );
-		serverUri[applicationDescriptionArray[0].applicationUri.length] = '\0';
-		ck_assert_str_eq(serverUri, "open62541.test.local_discovery_server");
+	for (size_t i=0; i<expectedUrisSize; i++) {
+		char* serverUri = malloc(sizeof(char)*applicationDescriptionArray[i].applicationUri.length+1);
+		memcpy( serverUri, applicationDescriptionArray[i].applicationUri.data, applicationDescriptionArray[i].applicationUri.length );
+		serverUri[applicationDescriptionArray[i].applicationUri.length] = '\0';
+		ck_assert_str_eq(serverUri, expectedUris[i]);
 		free(serverUri);
+	}
 
-		UA_Array_delete(applicationDescriptionArray, applicationDescriptionArraySize, &UA_TYPES[UA_TYPES_APPLICATIONDESCRIPTION]);
+	UA_Array_delete(applicationDescriptionArray, applicationDescriptionArraySize, &UA_TYPES[UA_TYPES_APPLICATIONDESCRIPTION]);
 
-		UA_Client_disconnect(client);
-		UA_Client_delete(client);
+	UA_Client_disconnect(client);
+	UA_Client_delete(client);
+}
+
+// Test if discovery server lists himself as registered server, before any other registration.
+START_TEST(Client_find_discovery) {
+		const char* expectedUris[] ={"open62541.test.local_discovery_server"};
+		FindAndCheck(expectedUris, 1, NULL, NULL);
+	}
+END_TEST
+
+// Test if discovery server lists himself as registered server if it is filtered by his uri
+START_TEST(Client_filter_discovery) {
+		const char* expectedUris[] ={"open62541.test.local_discovery_server"};
+		FindAndCheck(expectedUris, 1, "open62541.test.local_discovery_server", "en");
 	}
 END_TEST
 
 // Test if registered server is returned from LDS
 START_TEST(Client_find_registered) {
-		UA_Client *client = UA_Client_new(UA_ClientConfig_standard);
-		UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
-
-		ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
-
-		UA_ApplicationDescription* applicationDescriptionArray = NULL;
-		size_t applicationDescriptionArraySize = 0;
-
-		retval = FindServers("opc.tcp://localhost:4840", &applicationDescriptionArraySize, &applicationDescriptionArray, NULL);
-		ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
 
-		// only the discovery server is expected
-		ck_assert_uint_eq(applicationDescriptionArraySize , 2);
-
-
-		char* serverUri = malloc(sizeof(char)*applicationDescriptionArray[0].applicationUri.length+1);
-		memcpy( serverUri, applicationDescriptionArray[0].applicationUri.data, applicationDescriptionArray[0].applicationUri.length );
-		serverUri[applicationDescriptionArray[0].applicationUri.length] = '\0';
-		ck_assert_str_eq(serverUri, "open62541.test.local_discovery_server");
-		free(serverUri);
-
-
-		serverUri = malloc(sizeof(char)*applicationDescriptionArray[1].applicationUri.length+1);
-		memcpy( serverUri, applicationDescriptionArray[1].applicationUri.data, applicationDescriptionArray[1].applicationUri.length );
-		serverUri[applicationDescriptionArray[1].applicationUri.length] = '\0';
-		ck_assert_str_eq(serverUri, "open62541.test.server_register");
-		free(serverUri);
-
-		UA_Array_delete(applicationDescriptionArray, applicationDescriptionArraySize, &UA_TYPES[UA_TYPES_APPLICATIONDESCRIPTION]);
-
-		UA_Client_disconnect(client);
-		UA_Client_delete(client);
+		const char* expectedUris[] ={"open62541.test.local_discovery_server", "open62541.test.server_register"};
+		FindAndCheck(expectedUris, 2, NULL, NULL);
 	}
 END_TEST
 
 // Test if filtering with uris works
 START_TEST(Client_find_filter) {
-		UA_Client *client = UA_Client_new(UA_ClientConfig_standard);
-		UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
-
-		ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
-
-		UA_ApplicationDescription* applicationDescriptionArray = NULL;
-		size_t applicationDescriptionArraySize = 0;
-
-		retval = FindServers("opc.tcp://localhost:4840", &applicationDescriptionArraySize, &applicationDescriptionArray, "open62541.test.server_register");
-		ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
-
-		// only the discovery server is expected
-		ck_assert_uint_eq(applicationDescriptionArraySize , 1);
-
-
-		char* serverUri = malloc(sizeof(char)*applicationDescriptionArray[0].applicationUri.length+1);
-		memcpy( serverUri, applicationDescriptionArray[0].applicationUri.data, applicationDescriptionArray[0].applicationUri.length );
-		serverUri[applicationDescriptionArray[0].applicationUri.length] = '\0';
-		ck_assert_str_eq(serverUri, "open62541.test.server_register");
-		free(serverUri);
-
-		UA_Array_delete(applicationDescriptionArray, applicationDescriptionArraySize, &UA_TYPES[UA_TYPES_APPLICATIONDESCRIPTION]);
+		const char* expectedUris[] ={"open62541.test.server_register"};
+		FindAndCheck(expectedUris, 1, "open62541.test.server_register", NULL);
+	}
+END_TEST
 
-		UA_Client_disconnect(client);
-		UA_Client_delete(client);
+START_TEST(Util_wait_timeout) {
+		// wait until server is removed by timeout. Additionally wait a few seconds more to be sure.
+		sleep(checkWait);
 	}
 END_TEST
 
@@ -244,6 +229,8 @@ static Suite* testSuite_Client(void) {
 	tcase_add_unchecked_fixture(tc_register, setup_lds, teardown_lds);
 	tcase_add_unchecked_fixture(tc_register, setup_register, teardown_register);
 	tcase_add_test(tc_register, Server_register);
+	// register two times
+	tcase_add_test(tc_register, Server_register);
 	tcase_add_test(tc_register, Server_unregister);
 	suite_add_tcase(s,tc_register);
 
@@ -256,7 +243,19 @@ static Suite* testSuite_Client(void) {
 	tcase_add_test(tc_register_find, Client_find_filter);
 	tcase_add_test(tc_register_find, Server_unregister);
 	tcase_add_test(tc_register_find, Client_find_discovery);
+	tcase_add_test(tc_register_find, Client_filter_discovery);
 	suite_add_tcase(s,tc_register_find);
+
+	// register server again, then wait for timeout and auto unregister
+	TCase *tc_register_timeout = tcase_create("RegisterServer timeout");
+	tcase_add_unchecked_fixture(tc_register_timeout, setup_lds, teardown_lds);
+	tcase_add_unchecked_fixture(tc_register_timeout, setup_register, teardown_register);
+	tcase_set_timeout(tc_register_timeout, checkWait+2);
+	tcase_add_test(tc_register_timeout, Server_register);
+	tcase_add_test(tc_register_timeout, Client_find_registered);
+	tcase_add_test(tc_register_timeout, Util_wait_timeout);
+	tcase_add_test(tc_register_timeout, Client_find_discovery);
+	suite_add_tcase(s,tc_register_timeout);
 	return s;
 }
 

+ 2 - 0
tests/check_server_userspace.c

@@ -17,6 +17,8 @@ START_TEST(Server_addNamespace_ShallWork)
     ck_assert_uint_gt(a, 0);
     ck_assert_uint_eq(a,b);
     ck_assert_uint_ne(a,c);
+
+	UA_Server_delete(server);
 }
 END_TEST
 

+ 3 - 1
tests/check_services_attributes.c

@@ -111,11 +111,13 @@ static UA_Server* makeTestSequence(void) {
     UA_MethodAttributes_init(&ma);
     ma.description = UA_LOCALIZEDTEXT("en_US", "Methodtest");
     ma.displayName = UA_LOCALIZEDTEXT("en_US", "Methodtest");
+    UA_QualifiedName qualifiedName = UA_QUALIFIEDNAME_ALLOC(0, "Methodtest");
     UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_METHODNODE),
                             UA_NODEID_NUMERIC(0, 3),
                             UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
-                            UA_QUALIFIEDNAME_ALLOC(0, "Methodtest"), ma,
+                            qualifiedName, ma,
                             NULL, NULL, 0, NULL, 0, NULL, NULL);
+    UA_QualifiedName_deleteMembers(&qualifiedName);
 #endif
 
 	return server;

+ 1 - 1
tests/check_services_nodemanagement.c

@@ -54,7 +54,7 @@ START_TEST(AddComplexTypeWithInheritance) {
   UA_QualifiedName myObjectName = UA_QUALIFIEDNAME(1, "the.fake.Server.Struct");
   UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
   UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
-  UA_Int32 handleCalled;
+  UA_Int32 handleCalled = 0;
   UA_InstantiationCallback iCallback = {.method=instantiationMethod, .handle = (void *) &handleCalled};
     
   UA_StatusCode res = UA_Server_addObjectNode(server, myObjectNodeId, parentNodeId, parentReferenceNodeId,