Julius Pfrommer преди 7 години
родител
ревизия
0ccac95559
променени са 3 файла, в които са добавени 270 реда и са изтрити 117 реда
  1. 241 72
      src/server/ua_services_view.c
  2. 4 8
      src/server/ua_subscription.c
  3. 25 37
      tests/check_services_view.c

+ 241 - 72
src/server/ua_services_view.c

@@ -446,12 +446,69 @@ UA_Server_browseNext(UA_Server *server, UA_Boolean releaseContinuationPoint,
 /* TranslateBrowsePath */
 /***********************/
 
-static UA_StatusCode
-walkBrowsePath(UA_Server *server, UA_Session *session, const UA_Node *node, const UA_RelativePath *path,
-               size_t pathindex, UA_BrowsePathTarget **targets, size_t *targets_size,
-               size_t *target_count) {
-    const UA_RelativePathElement *elem = &path->elements[pathindex];
-    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+static void
+walkBrowsePathElementNodeReference(UA_BrowsePathResult *result, size_t *targetsSize,
+                                   UA_NodeId **next, size_t *nextSize, size_t *nextCount,
+                                   UA_UInt32 elemDepth, UA_Boolean inverse, UA_Boolean all_refs,
+                                   const UA_NodeId *reftypes, size_t reftypes_count,
+                                   const UA_ReferenceNode *reference) {
+    /* Does the direction of the reference match? */
+    if(reference->isInverse != inverse)
+        return;
+
+    /* Is the node relevant? */
+    if(!all_refs) {
+        UA_Boolean match = false;
+        for(size_t j = 0; j < reftypes_count; ++j) {
+            if(UA_NodeId_equal(&reference->referenceTypeId, &reftypes[j])) {
+                match = true;
+                break;
+            }
+        }
+        if(!match)
+            return;
+    }
+
+    /* Does the reference point to an external server? Then add to the
+     * targets with the right path "depth" */
+    if(reference->targetId.serverIndex != 0) {
+        UA_BrowsePathTarget *tempTargets =
+            UA_realloc(result->targets, sizeof(UA_BrowsePathTarget) * (*targetsSize) * 2);
+        if(!tempTargets) {
+            result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
+            return;
+        }
+        result->targets = tempTargets;
+        (*targetsSize) *= 2;
+        result->statusCode = UA_ExpandedNodeId_copy(&reference->targetId,
+                                                    &result->targets[result->targetsSize].targetId);
+        result->targets[result->targetsSize].remainingPathIndex = elemDepth;
+        return;
+    }
+
+    /* Add the node to the next array for the following path element */
+    if(*nextSize <= *nextCount) {
+        UA_NodeId *tempNext = UA_realloc(*next, sizeof(UA_NodeId) * (*nextSize) * 2);
+        if(!tempNext) {
+            result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
+            return;
+        }
+        *next = tempNext;
+        (*nextSize) *= 2;
+    }
+    result->statusCode = UA_NodeId_copy(&reference->targetId.nodeId,
+                                        &(*next)[*nextCount]);
+    ++(*nextCount);
+}
+
+static void
+walkBrowsePathElement(UA_Server *server, UA_Session *session,
+                      UA_BrowsePathResult *result, size_t *targetsSize,
+                      const UA_RelativePathElement *elem, UA_UInt32 elemDepth,
+                      const UA_QualifiedName *targetName,
+                      const UA_NodeId *current, const size_t currentCount,
+                      UA_NodeId **next, size_t *nextSize, size_t *nextCount) {
+    /* Get the full list of relevant referencetypes for this path element */
     UA_NodeId *reftypes = NULL;
     size_t reftypes_count = 1; // all_refs or no subtypes => 1
     UA_Boolean all_refs = false;
@@ -462,104 +519,214 @@ walkBrowsePath(UA_Server *server, UA_Session *session, const UA_Node *node, cons
     } else {
         const UA_Node *rootRef = UA_NodeStore_get(server->nodestore, &elem->referenceTypeId);
         if(!rootRef || rootRef->nodeClass != UA_NODECLASS_REFERENCETYPE)
-            return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
-        retval = getTypeHierarchy(server->nodestore, rootRef, false, &reftypes, &reftypes_count);
+            return;
+        UA_StatusCode retval =
+            getTypeHierarchy(server->nodestore, rootRef, false, &reftypes, &reftypes_count);
         if(retval != UA_STATUSCODE_GOOD)
-            return retval;
+            return;
     }
 
-    for(size_t i = 0; i < node->referencesSize && retval == UA_STATUSCODE_GOOD; ++i) {
-        UA_Boolean match = all_refs;
-        for(size_t j = 0; j < reftypes_count && !match; ++j) {
-            if(node->references[i].isInverse == elem->isInverse &&
-               UA_NodeId_equal(&node->references[i].referenceTypeId, &reftypes[j]))
-                match = true;
+    /* Iterate over all nodes at the current depth-level */
+    for(size_t i = 0; i < currentCount; ++i) {
+        /* Get the node */
+        const UA_Node *node = UA_NodeStore_get(server->nodestore, &current[i]);
+        if(!node) {
+            /* If we cannot find the node at depth 0, the starting node does not exist */
+            if(elemDepth == 0)
+                result->statusCode = UA_STATUSCODE_BADNODEIDUNKNOWN;
+            continue;
         }
-        if(!match)
+
+        /* Test whether the current node has the target name required in the
+         * previous path element */
+        if(targetName && (targetName->namespaceIndex != node->browseName.namespaceIndex ||
+                          !UA_String_equal(&targetName->name, &node->browseName.name)))
             continue;
 
-        // get the node, todo: expandednodeid
-        const UA_Node *next = UA_NodeStore_get(server->nodestore, &node->references[i].targetId.nodeId);
-        if(!next)
+        /* Walk over the references in the node */
+        /* Loop over the nodes references */
+        for(size_t r = 0; r < node->referencesSize &&
+                result->statusCode == UA_STATUSCODE_GOOD; ++r) {
+            UA_ReferenceNode *reference = &node->references[r];
+            walkBrowsePathElementNodeReference(result, targetsSize, next, nextSize, nextCount,
+                                               elemDepth, elem->isInverse, all_refs,
+                                               reftypes, reftypes_count, reference);
+        }
+    }
+
+    if(!all_refs && elem->includeSubtypes)
+        UA_Array_delete(reftypes, reftypes_count, &UA_TYPES[UA_TYPES_NODEID]);
+}
+
+/* This assumes that result->targets has enough room for all currentCount elements */
+static void
+addBrowsePathTargets(UA_Server *server, UA_Session *session, UA_BrowsePathResult *result,
+                     const UA_QualifiedName *targetName, UA_NodeId *current, size_t currentCount) {
+    for(size_t i = 0; i < currentCount; i++) {
+        /* Get the node */
+        const UA_Node *node = UA_NodeStore_get(server->nodestore, &current[i]);
+        if(!node) {
+            UA_NodeId_deleteMembers(&current[i]);
             continue;
+        }
 
-        // test the browsename
-        if(elem->targetName.namespaceIndex != next->browseName.namespaceIndex ||
-           !UA_String_equal(&elem->targetName.name, &next->browseName.name)) {
+        /* Test whether the current node has the target name required in the
+         * previous path element */
+        if(targetName->namespaceIndex != node->browseName.namespaceIndex ||
+           !UA_String_equal(&targetName->name, &node->browseName.name)) {
+            UA_NodeId_deleteMembers(&current[i]);
             continue;
         }
 
-        if(pathindex + 1 < path->elementsSize) {
-            // recursion if the path is longer
-            retval = walkBrowsePath(server, session, next, path, pathindex + 1,
-                                    targets, targets_size, target_count);
-        } else {
-            // add the browsetarget
-            if(*target_count >= *targets_size) {
-                UA_BrowsePathTarget *newtargets;
-                newtargets = (UA_BrowsePathTarget *)UA_realloc(targets, sizeof(UA_BrowsePathTarget) * (*targets_size) * 2);
-                if(!newtargets) {
-                    retval = UA_STATUSCODE_BADOUTOFMEMORY;
-                    break;
-                }
-                *targets = newtargets;
-                *targets_size *= 2;
-            }
+        /* Move the nodeid to the target array */
+        UA_BrowsePathTarget_init(&result->targets[result->targetsSize]);
+        result->targets[result->targetsSize].targetId.nodeId = current[i];
+        result->targets[result->targetsSize].remainingPathIndex = UA_UINT32_MAX;
+        ++result->targetsSize;
+    }
+}
 
-            UA_BrowsePathTarget *res = *targets;
-            UA_ExpandedNodeId_init(&res[*target_count].targetId);
-            retval = UA_NodeId_copy(&next->nodeId, &res[*target_count].targetId.nodeId);
-            if(retval != UA_STATUSCODE_GOOD)
-                break;
-            res[*target_count].remainingPathIndex = UA_UINT32_MAX;
-            *target_count += 1;
+static void
+walkBrowsePath(UA_Server *server, UA_Session *session, const UA_BrowsePath *path,
+               UA_BrowsePathResult *result, size_t targetsSize,
+               UA_NodeId **current, size_t *currentSize, size_t *currentCount,
+               UA_NodeId **next, size_t *nextSize, size_t *nextCount) {
+    UA_assert(*currentCount == 1);
+    UA_assert(*nextCount == 0);
+
+    /* Points to the targetName of the _previous_ path element */
+    const UA_QualifiedName *targetName = NULL;
+
+    /* Iterate over path elements */
+    UA_assert(path->relativePath.elementsSize > 0);
+    for(UA_UInt32 i = 0; i < path->relativePath.elementsSize; ++i) {
+        walkBrowsePathElement(server, session, result, &targetsSize,
+                              &path->relativePath.elements[i], i, targetName,
+                              *current, *currentCount, next, nextSize, nextCount);
+
+        /* Clean members of current */
+        for(size_t j = 0; j < *currentCount; j++)
+            UA_NodeId_deleteMembers(&(*current)[j]);
+        *currentCount = 0;
+
+        /* When no targets are left or an error occurred. None of next's
+         * elements will be copied to result->targets */
+        if(nextCount == 0 || result->statusCode != UA_STATUSCODE_GOOD) {
+            UA_assert(*currentCount == 0);
+            UA_assert(*nextCount == 0);
+            return;
         }
+
+        /* Exchange current and next for the next depth */
+        size_t tSize = *currentSize; size_t tCount = *currentCount; UA_NodeId *tT = *current;
+        *currentSize = *nextSize; *currentCount = *nextCount; *current = *next;
+        *nextSize = tSize; *nextCount = tCount; *next = tT;
+
+        /* Store the target name of the previous path element */
+        targetName = &path->relativePath.elements[i].targetName;
     }
 
-    if(!all_refs && elem->includeSubtypes)
-        UA_Array_delete(reftypes, reftypes_count, &UA_TYPES[UA_TYPES_NODEID]);
-    return retval;
+    UA_assert(targetName != NULL);
+    UA_assert(*nextCount == 0);
+
+    /* After the last BrowsePathElement, move members from current to the
+     * result targets */
+
+    /* Realloc if more space is needed */
+    if(targetsSize < result->targetsSize + (*currentCount)) {
+        UA_BrowsePathTarget *newTargets =
+            (UA_BrowsePathTarget*)UA_realloc(result->targets, sizeof(UA_BrowsePathTarget) *
+                                             (result->targetsSize + (*currentCount)));
+        if(!newTargets) {
+            result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
+            for(size_t i = 0; i < *currentCount; ++i)
+                UA_NodeId_deleteMembers(&(*current)[i]);
+            *currentCount = 0;
+            return;
+        }
+        result->targets = newTargets;
+    }
+
+    /* Move the elements of current to the targets */
+    addBrowsePathTargets(server, session, result, targetName, *current, *currentCount);
+    *currentCount = 0;
 }
 
 static void
-translateBrowsePathsToNodeIds(UA_Server *server, UA_Session *session,
-                              const UA_BrowsePath *path, UA_BrowsePathResult *result) {
+translateBrowsePathToNodeIds(UA_Server *server, UA_Session *session,
+                             const UA_BrowsePath *path, UA_BrowsePathResult *result) {
     if(path->relativePath.elementsSize <= 0) {
         result->statusCode = UA_STATUSCODE_BADNOTHINGTODO;
         return;
     }
-
-    //relativePath elements should not have an empty targetName
-    for(size_t i=0;i<path->relativePath.elementsSize;++i){
-        UA_QualifiedName *qname = &(path->relativePath.elements[i].targetName);
-        if(UA_QualifiedName_isNull(qname)){
+        
+    /* RelativePath elements must not have an empty targetName */
+    for(size_t i = 0; i < path->relativePath.elementsSize; ++i) {
+        if(UA_QualifiedName_isNull(&path->relativePath.elements[i].targetName)) {
             result->statusCode = UA_STATUSCODE_BADBROWSENAMEINVALID;
             return;
         }
     }
 
-    size_t arraySize = 10;
-    result->targets = (UA_BrowsePathTarget *)UA_malloc(sizeof(UA_BrowsePathTarget) * arraySize);
+    /* Allocate memory for the targets */
+    size_t targetsSize = 10; /* When to realloc; the member count is stored in
+                              * result->targetsSize */
+    result->targets = (UA_BrowsePathTarget*)UA_malloc(sizeof(UA_BrowsePathTarget) * targetsSize);
     if(!result->targets) {
         result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
         return;
     }
-    result->targetsSize = 0;
-    const UA_Node *firstNode = UA_NodeStore_get(server->nodestore, &path->startingNode);
-    if(!firstNode) {
-        result->statusCode = UA_STATUSCODE_BADNODEIDUNKNOWN;
+
+    /* Allocate memory for two temporary arrays. One with the results for the
+     * previous depth of the path. The other for the new results at the current
+     * depth. The two arrays alternate as we descend down the tree. */
+    size_t currentSize = 10; /* When to realloc */
+    size_t currentCount = 0; /* Current elements */
+    UA_NodeId *current = (UA_NodeId*)UA_malloc(sizeof(UA_NodeId) * currentSize);
+    if(!current) {
+        result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
         UA_free(result->targets);
-        result->targets = NULL;
         return;
     }
+    size_t nextSize = 10; /* When to realloc */
+    size_t nextCount = 0; /* Current elements */
+    UA_NodeId *next = (UA_NodeId*)UA_malloc(sizeof(UA_NodeId) * nextSize);
+    if(!next) {
+        result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
+        UA_free(result->targets);
+        UA_free(current);
+        return;
+    }
+
+    /* Copy the starting node into current */
+    result->statusCode = UA_NodeId_copy(&path->startingNode, &current[0]);
+    if(result->statusCode != UA_STATUSCODE_GOOD) {
+        UA_free(result->targets);
+        UA_free(current);
+        UA_free(next);
+        return;
+    }
+    currentCount = 1;
+
+    /* Walk the path elements */
+    walkBrowsePath(server, session, path, result, targetsSize,
+                   &current, &currentSize, &currentCount,
+                   &next, &nextSize, &nextCount);
 
-    result->statusCode = walkBrowsePath(server, session, firstNode, &path->relativePath, 0,
-                                        &result->targets, &arraySize, &result->targetsSize);
+    UA_assert(currentCount == 0);
+    UA_assert(nextCount == 0);
+
+    /* No results => BadNoMatch status code */
     if(result->targetsSize == 0 && result->statusCode == UA_STATUSCODE_GOOD)
         result->statusCode = UA_STATUSCODE_BADNOMATCH;
 
+    /* Clean up the temporary arrays and the targets */
+    UA_free(current);
+    UA_free(next);
     if(result->statusCode != UA_STATUSCODE_GOOD) {
-        UA_Array_delete(result->targets, result->targetsSize, &UA_TYPES[UA_TYPES_BROWSEPATHTARGET]);
+        for(size_t i = 0; i < result->targetsSize; ++i)
+            UA_BrowsePathTarget_deleteMembers(&result->targets[i]);
+        UA_free(result->targets);
         result->targets = NULL;
         result->targetsSize = 0;
     }
@@ -571,7 +738,7 @@ UA_Server_translateBrowsePathToNodeIds(UA_Server *server,
     UA_BrowsePathResult result;
     UA_BrowsePathResult_init(&result);
     UA_RCU_LOCK();
-    translateBrowsePathsToNodeIds(server, &adminSession, browsePath, &result);
+    translateBrowsePathToNodeIds(server, &adminSession, browsePath, &result);
     UA_RCU_UNLOCK();
     return result;
 }
@@ -617,16 +784,18 @@ void Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *sessio
                                            indices, (UA_UInt32)indexSize, response->results,
                                            response->diagnosticInfos);
     }
-#endif
-
     response->resultsSize = size;
     for(size_t i = 0; i < size; ++i) {
-#ifdef UA_ENABLE_EXTERNAL_NAMESPACES
         if(!isExternal[i])
-#endif
-            translateBrowsePathsToNodeIds(server, session, &request->browsePaths[i],
-                                          &response->results[i]);
+            translateBrowsePathToNodeIds(server, session, &request->browsePaths[i],
+                                         &response->results[i]);
     }
+#else
+    response->resultsSize = size;
+    for(size_t i = 0; i < size; ++i)
+        translateBrowsePathToNodeIds(server, session, &request->browsePaths[i],
+                                     &response->results[i]);
+#endif
 }
 
 void Service_RegisterNodes(UA_Server *server, UA_Session *session, const UA_RegisterNodesRequest *request,

+ 4 - 8
src/server/ua_subscription.c

@@ -103,10 +103,12 @@ detectValueChange(UA_MonitoredItem *mon, UA_DataValue *value, UA_ByteString *enc
     UA_Boolean hasValue = value->hasValue;
     if(mon->trigger == UA_DATACHANGETRIGGER_STATUS)
         value->hasValue = false;
+
     UA_Boolean hasServerTimestamp = value->hasServerTimestamp;
     UA_Boolean hasServerPicoseconds = value->hasServerPicoseconds;
     value->hasServerTimestamp = false;
     value->hasServerPicoseconds = false;
+
     UA_Boolean hasSourceTimestamp = value->hasSourceTimestamp;
     UA_Boolean hasSourcePicoseconds = value->hasSourcePicoseconds;
     if(mon->trigger < UA_DATACHANGETRIGGER_STATUSVALUETIMESTAMP) {
@@ -208,13 +210,6 @@ void UA_MoniteredItem_SampleCallback(UA_Server *server, UA_MonitoredItem *monito
         return;
     }
 
-    /* Adjust timestampstoreturn to get source timestamp for triggering */
-    UA_TimestampsToReturn ts = monitoredItem->timestampsToReturn;
-    if(ts == UA_TIMESTAMPSTORETURN_SERVER)
-        ts = UA_TIMESTAMPSTORETURN_BOTH;
-    else if(ts == UA_TIMESTAMPSTORETURN_NEITHER)
-        ts = UA_TIMESTAMPSTORETURN_SOURCE;
-
     /* Read the value */
     UA_ReadValueId rvid;
     UA_ReadValueId_init(&rvid);
@@ -223,7 +218,8 @@ void UA_MoniteredItem_SampleCallback(UA_Server *server, UA_MonitoredItem *monito
     rvid.indexRange = monitoredItem->indexRange;
     UA_DataValue value;
     UA_DataValue_init(&value);
-    Service_Read_single(server, sub->session, ts, &rvid, &value);
+    Service_Read_single(server, sub->session, monitoredItem->timestampsToReturn,
+                        &rvid, &value);
 
     /* Stack-allocate some memory for the value encoding. We might heap-allocate
      * more memory if needed. This is just enough for scalars and small

+ 25 - 37
tests/check_services_view.c

@@ -70,8 +70,6 @@ START_TEST(Service_Browse_WithBrowseName)
     }
 END_TEST
 
-#define BROWSE_PATHS_SIZE 3
-
 START_TEST(Service_TranslateBrowsePathsToNodeIds)
     {
         UA_Client *client = UA_Client_new(UA_ClientConfig_standard);
@@ -84,50 +82,40 @@ START_TEST(Service_TranslateBrowsePathsToNodeIds)
         // Equals the following node IDs:
         // /85/2253/2256/2259
 
+#define BROWSE_PATHS_SIZE 3
         char *paths[BROWSE_PATHS_SIZE] = {"Server", "ServerStatus", "State"};
-        UA_UInt32 ids[BROWSE_PATHS_SIZE] = {2253, 2256, 2259};
-
-
-        UA_BrowsePath *browsePaths = UA_Array_new(BROWSE_PATHS_SIZE, &UA_TYPES[UA_TYPES_BROWSEPATH]);
-
-        for (unsigned int i = 0; i < BROWSE_PATHS_SIZE; i++) {
-            UA_BrowsePath_init(&browsePaths[i]);
-            browsePaths[i].startingNode = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
-            browsePaths[i].relativePath.elementsSize = i + 1;
-            browsePaths[i].relativePath.elements = UA_Array_new(i + 1, &UA_TYPES[UA_TYPES_RELATIVEPATHELEMENT]);
-            for (unsigned int j = 0; j <= i; j++) {
-                UA_RelativePathElement_init(&browsePaths[i].relativePath.elements[j]);
-                browsePaths[i].relativePath.elements[j].isInverse = UA_TRUE;
-
-                browsePaths[i].relativePath.elements[j].targetName = UA_QUALIFIEDNAME_ALLOC(0, paths[j]);
-            }
+        UA_UInt32 ids[BROWSE_PATHS_SIZE] = {UA_NS0ID_ORGANIZES, UA_NS0ID_HASCOMPONENT, UA_NS0ID_HASCOMPONENT};
+        UA_BrowsePath browsePath;
+        UA_BrowsePath_init(&browsePath);
+        browsePath.startingNode = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
+        browsePath.relativePath.elements = UA_Array_new(BROWSE_PATHS_SIZE, &UA_TYPES[UA_TYPES_RELATIVEPATHELEMENT]);
+        browsePath.relativePath.elementsSize = BROWSE_PATHS_SIZE;
+
+        for(size_t i = 0; i < BROWSE_PATHS_SIZE; i++) {
+            UA_RelativePathElement *elem = &browsePath.relativePath.elements[i];
+            elem->referenceTypeId = UA_NODEID_NUMERIC(0, ids[i]);
+            elem->targetName = UA_QUALIFIEDNAME_ALLOC(0, paths[i]);
         }
 
         UA_TranslateBrowsePathsToNodeIdsRequest request;
         UA_TranslateBrowsePathsToNodeIdsRequest_init(&request);
-        request.browsePaths = browsePaths;
-        request.browsePathsSize = BROWSE_PATHS_SIZE;
-
-        {
-
-            UA_TranslateBrowsePathsToNodeIdsResponse response = UA_Client_Service_translateBrowsePathsToNodeIds(client, request);
+        request.browsePaths = &browsePath;
+        request.browsePathsSize = 1;
 
-            ck_assert_int_eq(response.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
-            ck_assert_int_eq(response.resultsSize, BROWSE_PATHS_SIZE);
+        UA_TranslateBrowsePathsToNodeIdsResponse response = UA_Client_Service_translateBrowsePathsToNodeIds(client, request);
 
-            for (int i = 0; i < BROWSE_PATHS_SIZE; i++) {
-                ck_assert_int_eq(response.results[i].targetsSize, 1);
-                ck_assert_int_eq(response.results[i].targets[0].targetId.nodeId.identifierType, UA_NODEIDTYPE_NUMERIC);
-                ck_assert_int_eq(response.results[i].targets[0].targetId.nodeId.identifier.numeric, ids[i]);
+        ck_assert_int_eq(response.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
+        ck_assert_int_eq(response.resultsSize, 1);
 
-            }
+        ck_assert_int_eq(response.results[0].targetsSize, 1);
+        ck_assert_int_eq(response.results[0].targets[0].targetId.nodeId.identifierType, UA_NODEIDTYPE_NUMERIC);
+        ck_assert_int_eq(response.results[0].targets[0].targetId.nodeId.identifier.numeric, UA_NS0ID_SERVER_SERVERSTATUS_STATE);
 
-            UA_TranslateBrowsePathsToNodeIdsRequest_deleteMembers(&request);
-            UA_TranslateBrowsePathsToNodeIdsResponse_deleteMembers(&response);
-            retVal = UA_Client_disconnect(client);
-            ck_assert_int_eq(retVal, UA_STATUSCODE_GOOD);
-            UA_Client_delete(client);
-        }
+        UA_BrowsePath_deleteMembers(&browsePath);
+        UA_TranslateBrowsePathsToNodeIdsResponse_deleteMembers(&response);
+        retVal = UA_Client_disconnect(client);
+        ck_assert_int_eq(retVal, UA_STATUSCODE_GOOD);
+        UA_Client_delete(client);
     }
 END_TEST