Selaa lähdekoodia

Merge branch 'master' of https://github.com/acplt/open62541

ichrispa 9 vuotta sitten
vanhempi
commit
60a46e6abf

+ 3 - 3
CMakeLists.txt

@@ -18,13 +18,13 @@ find_package(Git)
 if(GIT_FOUND)
     execute_process(COMMAND ${GIT_EXECUTABLE} describe --abbrev=7 --dirty --always --tags RESULT_VARIABLE res_var OUTPUT_VARIABLE GIT_COM_ID )
     if( NOT ${res_var} EQUAL 0 )
-        set( GIT_COMMIT_ID "git commit id unknown")
+        set( GIT_COMMIT_ID "unknown--git-commit-id-unknown")
         message( WARNING "Git failed (not a repo, or no tags). Build will not contain git revision info." )
     else()
         string( REPLACE "\n" "" GIT_COMMIT_ID ${GIT_COM_ID} )
     endif()
 else()
-    set( GIT_COMMIT_ID "unknown (git not found!)")
+    set( GIT_COMMIT_ID "unknown--no-git-found")
     message( WARNING "Git not found. Build will not contain git revision info." )
 endif()
 add_definitions("-DVERSION=${GIT_COMMIT_ID}")
@@ -216,11 +216,11 @@ add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/open62541.c
                DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/amalgamate.py ${internal_headers} ${PROJECT_SOURCE_DIR}/src/server/ua_nodestore_hash.inc ${lib_sources})
 
 if(ENABLE_AMALGAMATION)
-    add_definitions(-DUA_AMALGAMATE)
     add_custom_target(amalgamation ALL DEPENDS ${PROJECT_BINARY_DIR}/open62541.h ${PROJECT_BINARY_DIR}/open62541.c)
     add_library(open62541-object OBJECT ${PROJECT_BINARY_DIR}/open62541.c ${PROJECT_BINARY_DIR}/open62541.h)
     include_directories(${PROJECT_BINARY_DIR})
 else()
+    add_definitions(-DUA_NO_AMALGAMATION)
     add_library(open62541-object OBJECT ${lib_sources} ${internal_headers} ${exported_headers})
     include_directories(${PROJECT_SOURCE_DIR}/include)
     include_directories(${PROJECT_SOURCE_DIR}/deps)

+ 5 - 5
examples/client.c

@@ -1,14 +1,14 @@
-#ifdef UA_AMALGAMATE
-# include "open62541.h"
-# include <string.h>
-# include <stdlib.h>
-#else
+#ifdef UA_NO_AMALGAMATION
 # include "ua_types.h"
 # include "ua_client.h"
 # include "ua_nodeids.h"
 # include "networklayer_tcp.h"
 # include "logger_stdout.h"
 # include "ua_types_encoding_binary.h"
+#else
+# include "open62541.h"
+# include <string.h>
+# include <stdlib.h>
 #endif
 
 #include <stdio.h>

+ 61 - 136
examples/networklayer_tcp.c

@@ -57,32 +57,42 @@ static UA_StatusCode socket_write(UA_Connection *connection, UA_ByteString *buf,
     return UA_STATUSCODE_GOOD;
 }
 
+static void socket_close(UA_Connection *connection) {
+    connection->state = UA_CONNECTION_CLOSED;
+    shutdown(connection->sockfd,2);
+    CLOSESOCKET(connection->sockfd);
+}
+
 static UA_StatusCode socket_recv(UA_Connection *connection, UA_ByteString *response, UA_UInt32 timeout) {
     response->data = malloc(connection->localConf.recvBufferSize);
-    if(!response->data)
-        return UA_STATUSCODE_BADOUTOFMEMORY;
+    if(!response->data) {
+        UA_ByteString_init(response);
+        return UA_STATUSCODE_GOOD; /* not enough memory retry */
+    }
     struct timeval tmptv = {0, timeout * 1000};
     if(0 != setsockopt(connection->sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tmptv, sizeof(struct timeval))){
 		free(response->data);
+        UA_ByteString_init(response);
+        socket_close(connection);
         return UA_STATUSCODE_BADINTERNALERROR;
     }
     int ret = recv(connection->sockfd, (char*)response->data, connection->localConf.recvBufferSize, 0);
 	if(ret == 0) {
 		free(response->data);
         UA_ByteString_init(response);
-		return UA_STATUSCODE_GOOD; /* no response -> retry */
+        socket_close(connection);
+        return UA_CONNECTION_CLOSED; /* ret == 0 -> server has closed the connection */
 	} else if(ret < 0) {
         free(response->data);
         UA_ByteString_init(response);
 #ifdef _WIN32
 		if(WSAGetLastError() == WSAEINTR || WSAGetLastError() == WSAEWOULDBLOCK) {
 #else
-		if (errno == EAGAIN || errno == EWOULDBLOCK) {
+		if(errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) {
 #endif
             return UA_STATUSCODE_GOOD; /* retry */
         } else {
-            connection->close(connection);
-            connection->state = UA_CONNECTION_CLOSED;
+            socket_close(connection);
             return UA_STATUSCODE_BADCONNECTIONCLOSED;
         }
     }
@@ -91,12 +101,6 @@ static UA_StatusCode socket_recv(UA_Connection *connection, UA_ByteString *respo
     return UA_STATUSCODE_GOOD;
 }
 
-static void socket_close(UA_Connection *connection) {
-    connection->state = UA_CONNECTION_CLOSED;
-    shutdown(connection->sockfd,2);
-    CLOSESOCKET(connection->sockfd);
-}
-
 static UA_StatusCode socket_set_nonblocking(UA_Int32 sockfd) {
 #ifdef _WIN32
     u_long iMode = 1;
@@ -110,6 +114,11 @@ static UA_StatusCode socket_set_nonblocking(UA_Int32 sockfd) {
     return UA_STATUSCODE_GOOD;
 }
 
+static void FreeConnectionCallback(UA_Server *server, void *ptr) {
+    UA_Connection_deleteMembers((UA_Connection*)ptr);
+     free(ptr);
+ }
+
 /***************************/
 /* Server NetworkLayer TCP */
 /***************************/
@@ -165,12 +174,6 @@ typedef struct {
         UA_Connection *connection;
         UA_Int32 sockfd;
     } *mappings;
-
-    /* to-be-deleted connections */
-    struct DeleteList {
-        struct DeleteList *next;
-        UA_Connection *connection;
-    } *deletes;
 } ServerNetworkLayerTCP;
 
 static UA_StatusCode ServerNetworkLayerGetBuffer(UA_Connection *connection, UA_ByteString *buf) {
@@ -219,23 +222,8 @@ static void ServerNetworkLayerTCP_closeConnection(UA_Connection *connection) {
         return;
     connection->state = UA_CONNECTION_CLOSED;
 #endif
-    socket_close(connection);
-    ServerNetworkLayerTCP *layer = (ServerNetworkLayerTCP*)connection->handle;
-    struct DeleteList *d = malloc(sizeof(struct DeleteList));
-    if(!d){
-        return;
-    }
-    d->connection = connection;
-#ifdef UA_MULTITHREADING
-    while(1) {
-        d->next = layer->deletes;
-        if(uatomic_cmpxchg(&layer->deletes, d->next, d) == d->next)
-            break;
-    }
-#else
-    d->next = layer->deletes;
-    layer->deletes = d;
-#endif
+    shutdown(connection->sockfd, 2); /* only shut down here. this triggers the select, where the socket
+                                        is closed in the main thread */
 }
 
 /* call only from the single networking thread */
@@ -303,48 +291,19 @@ static UA_StatusCode ServerNetworkLayerTCP_start(UA_ServerNetworkLayer *nl, UA_L
     return UA_STATUSCODE_GOOD;
 }
 
-/* delayed callback that frees old connections */
-static void freeConnections(UA_Server *server, struct DeleteList *d) {
-    while(d) {
-        UA_Connection_deleteMembers(d->connection);
-        free(d->connection);
-        struct DeleteList *old = d;
-        d = d->next;
-        free(old);
-    }
-}
-
-/* remove the closed sockets from the mappings array */
-static void removeMappings(ServerNetworkLayerTCP *layer, struct DeleteList *d) {
-    while(d) {
-        size_t i = 0;
-        for(; i < layer->mappingsSize; i++) {
-            if(layer->mappings[i].sockfd == d->connection->sockfd)
-                break;
-        }
-        if(i >= layer->mappingsSize)
-            continue;
-        layer->mappingsSize--;
-        layer->mappings[i] = layer->mappings[layer->mappingsSize];
-        d = d->next;
-    }
-}
-
 static UA_Int32 ServerNetworkLayerTCP_getJobs(UA_ServerNetworkLayer *nl, UA_Job **jobs, UA_UInt16 timeout) {
     ServerNetworkLayerTCP *layer = nl->handle;
-    /* remove the deleted sockets from the array */
-    struct DeleteList *deletes;
-#ifdef UA_MULTITHREADING
-        deletes = uatomic_xchg(&layer->deletes, NULL);
-#else
-        deletes = layer->deletes;
-        layer->deletes = NULL;
-#endif
-    removeMappings(layer, deletes);
-
     setFDSet(layer);
     struct timeval tmptv = {0, timeout};
-    UA_Int32 resultsize = select(layer->highestfd+1, &layer->fdset, NULL, NULL, &tmptv);
+    UA_Int32 resultsize;
+ repeat_select:
+    resultsize = select(layer->highestfd+1, &layer->fdset, NULL, NULL, &tmptv);
+    if(resultsize < 0) {
+        if(errno == EINTR)
+            goto repeat_select;
+        *jobs = NULL;
+        return resultsize;
+    }
 
     /* accept new connections (can only be a single one) */
     if(FD_ISSET(layer->serversockfd, &layer->fdset)) {
@@ -354,40 +313,18 @@ static UA_Int32 ServerNetworkLayerTCP_getJobs(UA_ServerNetworkLayer *nl, UA_Job
         int newsockfd = accept(layer->serversockfd, (struct sockaddr *) &cli_addr, &cli_len);
         int i = 1;
         setsockopt(newsockfd, IPPROTO_TCP, TCP_NODELAY, (void *)&i, sizeof(i));
-        if (newsockfd >= 0) {
+        if(newsockfd >= 0) {
             socket_set_nonblocking(newsockfd);
             ServerNetworkLayerTCP_add(layer, newsockfd);
         }
     }
 
-    if(!deletes && resultsize <= 0) {
-        *jobs = NULL;
+    /* alloc enough space for a cleanup-connection and free-connection job per resulted socket */
+    if(resultsize == 0)
+        return 0;
+    UA_Job *js = malloc(sizeof(UA_Job) * resultsize * 2);
+    if(!js)
         return 0;
-    }
-    if(resultsize < 0)
-        resultsize = 0;
-    UA_Int32 deletesJob = 0;
-    if(deletes)
-        deletesJob = 1;
-        
-    UA_Job *items = malloc(sizeof(UA_Job) * (resultsize + deletesJob));
-    if(deletes && !items) {
-        /* abort. reattach the deletes so that they get deleted eventually. */
-#ifdef UA_MULTITHREADING
-        struct DeleteList *last_delete;
-        while(deletes) {
-            last_delete = deletes;
-            deletes = deletes->next;
-        }
-        while(1) {
-            last_delete->next = layer->deletes;
-            if(uatomic_cmpxchg(&layer->deletes, last_delete->next, deletes) == last_delete->next)
-                break;
-        }
-#else
-        layer->deletes = deletes;
-#endif
-    }
 
     /* read from established sockets */
     UA_Int32 j = 0;
@@ -398,63 +335,52 @@ static UA_Int32 ServerNetworkLayerTCP_getJobs(UA_ServerNetworkLayer *nl, UA_Job
         if(socket_recv(layer->mappings[i].connection, &buf, 0) == UA_STATUSCODE_GOOD) {
             if(!buf.data)
                 continue;
-            items[j].type = UA_JOBTYPE_BINARYMESSAGE;
-            items[j].job.binaryMessage.message = buf;
-            items[j].job.binaryMessage.connection = layer->mappings[i].connection;
-            buf.data = NULL;
+            js[j].type = UA_JOBTYPE_BINARYMESSAGE;
+            js[j].job.binaryMessage.message = buf;
+            js[j].job.binaryMessage.connection = layer->mappings[i].connection;
         } else {
-            items[j].type = UA_JOBTYPE_CLOSECONNECTION;
-            items[j].job.closeConnection = layer->mappings[i].connection;
+            UA_Connection *c = layer->mappings[i].connection;
+            /* the socket is already closed */
+            js[j].type = UA_JOBTYPE_DETACHCONNECTION;
+            js[j].job.closeConnection = layer->mappings[i].connection;
+            layer->mappings[i] = layer->mappings[layer->mappingsSize-1];
+            layer->mappingsSize--;
+            j++;
+            i--; // iterate over the same index again
+            js[j].type = UA_JOBTYPE_DELAYEDMETHODCALL;
+            js[j].job.methodCall.method = FreeConnectionCallback;
+            js[j].job.methodCall.data = c;
         }
         j++;
     }
 
-    /* add the delayed job that frees the connections */
-    if(deletes) {
-        items[j].type = UA_JOBTYPE_DELAYEDMETHODCALL;
-        items[j].job.methodCall.data = deletes;
-        items[j].job.methodCall.method = (void (*)(UA_Server *server, void *data))freeConnections;
-        j++;
-    }
-
-    /* free the array if there is no job */
-    if(j == 0) {
-        free(items);
-        *jobs = NULL;
-    } else
-        *jobs = items;
+    *jobs = js;
     return j;
 }
 
 static UA_Int32 ServerNetworkLayerTCP_stop(UA_ServerNetworkLayer *nl, UA_Job **jobs) {
     ServerNetworkLayerTCP *layer = nl->handle;
-    struct DeleteList *deletes;
-#ifdef UA_MULTITHREADING
-        deletes = uatomic_xchg(&layer->deletes, NULL);
-#else
-        deletes = layer->deletes;
-        layer->deletes = NULL;
-#endif
-    removeMappings(layer, deletes);
-    UA_Job *items = malloc(sizeof(UA_Job) * layer->mappingsSize);
+    UA_Job *items = malloc(sizeof(UA_Job) * layer->mappingsSize * 2);
     if(!items)
         return 0;
     for(size_t i = 0; i < layer->mappingsSize; i++) {
-        items[i].type = UA_JOBTYPE_CLOSECONNECTION;
-        items[i].job.closeConnection = layer->mappings[i].connection;
+        socket_close(layer->mappings[i].connection);
+        items[i*2].type = UA_JOBTYPE_DETACHCONNECTION;
+        items[i*2].job.closeConnection = layer->mappings[i].connection;
+        items[(i*2)+1].type = UA_JOBTYPE_DELAYEDMETHODCALL;
+        items[(i*2)+1].job.methodCall.method = FreeConnectionCallback;
+        items[(i*2)+1].job.methodCall.data = layer->mappings[i].connection;
     }
 #ifdef _WIN32
     WSACleanup();
 #endif
     *jobs = items;
-    return layer->mappingsSize;
+    return layer->mappingsSize*2;
 }
 
 /* run only when the server is stopped */
 static void ServerNetworkLayerTCP_deleteMembers(UA_ServerNetworkLayer *nl) {
     ServerNetworkLayerTCP *layer = nl->handle;
-    removeMappings(layer, layer->deletes);
-    freeConnections(NULL, layer->deletes);
 #ifndef UA_MULTITHREADING
     UA_ByteString_deleteMembers(&layer->buffer);
 #endif
@@ -482,7 +408,6 @@ UA_ServerNetworkLayer ServerNetworkLayerTCP_new(UA_ConnectionConfig conf, UA_UIn
     layer->mappingsSize = 0;
     layer->mappings = NULL;
     layer->port = port;
-    layer->deletes = NULL;
     char hostname[256];
     gethostname(hostname, 255);
     UA_String_copyprintf("opc.tcp://%s:%d", &nl.discoveryUrl, hostname, port);

+ 3 - 3
examples/server.c

@@ -3,14 +3,14 @@
  * See http://creativecommons.org/publicdomain/zero/1.0/ for more information.
  */
 
-#ifdef UA_AMALGAMATE
-# include "open62541.h"
-#else
+#ifdef UA_NO_AMALGAMATION
 # include <time.h>
 # include "ua_types.h"
 # include "ua_server.h"
 # include "logger_stdout.h"
 # include "networklayer_tcp.h"
+#else
+# include "open62541.h"
 #endif
 
 #include <signal.h>

+ 3 - 3
examples/server_simple.c

@@ -9,13 +9,13 @@
 #include <errno.h> // errno, EINTR
 #include <string.h>
 
-#ifdef UA_AMALGAMATE
-# include "open62541.h"
-#else
+#ifdef UA_NO_AMALGAMATION
 # include "ua_types.h"
 # include "ua_server.h"
 # include "logger_stdout.h"
 # include "networklayer_tcp.h"
+#else
+# include "open62541.h"
 #endif
 
 UA_Boolean running = 1;

+ 1 - 1
include/ua_server.h

@@ -146,7 +146,7 @@ UA_Server_AddMonodirectionalReference(UA_Server *server, UA_NodeId sourceNodeId,
 typedef struct {
     enum {
         UA_JOBTYPE_NOTHING,
-        UA_JOBTYPE_CLOSECONNECTION,
+        UA_JOBTYPE_DETACHCONNECTION,
         UA_JOBTYPE_BINARYMESSAGE,
         UA_JOBTYPE_METHODCALL,
         UA_JOBTYPE_DELAYEDMETHODCALL,

+ 14 - 0
src/server/ua_server.c

@@ -59,6 +59,17 @@ static void UA_ExternalNamespace_deleteMembers(UA_ExternalNamespace *ens) {
     ens->externalNodeStore.destroy(ens->externalNodeStore.ensHandle);
 }
 
+static void UA_Server_deleteExternalNamespaces(UA_Server *server){
+	for(UA_UInt32 i = 0; i < server->externalNamespacesSize; i++){
+		UA_ExternalNamespace_deleteMembers(&(server->externalNamespaces[i]));
+	}
+	if(server->externalNamespacesSize > 0){
+		UA_free(server->externalNamespaces);
+		server->externalNamespaces = UA_NULL;
+		server->externalNamespacesSize = 0;
+	}
+}
+
 UA_StatusCode UA_EXPORT UA_Server_addExternalNamespace(UA_Server *server, UA_UInt16 namespaceIndex,
                                                        const UA_String *url, UA_ExternalNodeStore *nodeStore) {
 	if(nodeStore == UA_NULL)
@@ -146,6 +157,9 @@ void UA_Server_delete(UA_Server *server) {
     UA_SecureChannelManager_deleteMembers(&server->secureChannelManager);
     UA_SessionManager_deleteMembers(&server->sessionManager);
     UA_NodeStore_delete(server->nodestore);
+#ifdef UA_EXTERNAL_NAMESPACES
+    UA_Server_deleteExternalNamespaces(server);
+#endif
     UA_ByteString_deleteMembers(&server->serverCertificate);
     UA_Array_delete(server->namespaces, &UA_TYPES[UA_TYPES_STRING], server->namespacesSize);
     UA_Array_delete(server->endpointDescriptions, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION],

+ 1 - 2
src/server/ua_server_worker.c

@@ -35,9 +35,8 @@ static void processJobs(UA_Server *server, UA_Job *jobs, size_t jobsSize) {
             UA_Server_processBinaryMessage(server, job->job.binaryMessage.connection,
                                            &job->job.binaryMessage.message);
             break;
-        case UA_JOBTYPE_CLOSECONNECTION:
+        case UA_JOBTYPE_DETACHCONNECTION:
             UA_Connection_detachSecureChannel(job->job.closeConnection);
-            job->job.closeConnection->close(job->job.closeConnection);
             break;
         case UA_JOBTYPE_METHODCALL:
         case UA_JOBTYPE_DELAYEDMETHODCALL:

+ 12 - 1
src/server/ua_services.h

@@ -71,7 +71,7 @@ void Service_CloseSecureChannel(UA_Server *server, UA_Int32 channelId);
 /**
  * Used by an OPC UA Client to create a Session and the Server returns two
  * values which uniquely identify the Session. The first value is the sessionId
- * which is used to identify the Session in the audit logs and in the Servers
+ * which is used to identify the Session in the audit logs and in the Server's
  * address space. The second is the authenticationToken which is used to
  * associate an incoming request with a Session.
  */
@@ -188,6 +188,11 @@ void Service_UnregisterNodes(UA_Server *server, UA_Session *session, const UA_Un
  * @{
  */
 
+/* Mock-Up of the function signature for Unit Tests */
+#ifdef BUILD_UNIT_TESTS
+UA_StatusCode parse_numericrange(const UA_String str, UA_NumericRange *range);
+#endif
+
 /**
  * Used to read one or more Attributes of one or more Nodes. For constructed
  * Attribute values whose elements are indexed, such as an array, this Service
@@ -212,6 +217,12 @@ void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
  */
 void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest *request,
                    UA_WriteResponse *response);
+
+/* Mock-Up of the function signature for Unit Tests */
+#ifdef BUILD_UNIT_TESTS
+UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *wvalue);
+#endif
+
 // Service_HistoryUpdate
 /** @} */
 

+ 8 - 2
src/server/ua_services_attribute.c

@@ -5,7 +5,10 @@
 #include "ua_nodestore.h"
 #include "ua_util.h"
 
-static UA_StatusCode parse_numericrange(const UA_String str, UA_NumericRange *range) {
+#ifndef BUILD_UNIT_TESTS
+static
+#endif
+UA_StatusCode parse_numericrange(const UA_String str, UA_NumericRange *range) {
     if(str.length < 0 || str.length >= 1023)
         return UA_STATUSCODE_BADINTERNALERROR;
 #ifdef NO_ALLOCA
@@ -423,7 +426,10 @@ void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *
 #endif
 }
 
-static UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *wvalue) {
+#ifndef BUILD_UNIT_TESTS
+static
+#endif
+UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *wvalue) {
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
 
     /* is there a value at all */

+ 419 - 34
tests/check_services_attributes.c

@@ -46,16 +46,13 @@ static UA_Server* makeTestSequence(void) {
 	UA_Server_addObjectNode(server,UA_QUALIFIEDNAME(1, "Demo"), UA_NODEID_NUMERIC(1, 50), UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE));
 
 	/* ReferenceTypeNode */
-    UA_ReferenceTypeNode *hierarchicalreferences = UA_ReferenceTypeNode_new();
-    copyNames((UA_Node*)hierarchicalreferences, "Hierarchicalreferences");
-    hierarchicalreferences->nodeId.identifier.numeric = UA_NS0ID_HIERARCHICALREFERENCES;
-    hierarchicalreferences->isAbstract = UA_TRUE;
-    hierarchicalreferences->symmetric  = UA_FALSE;
-    hierarchicalreferences->inverseName = UA_LOCALIZEDTEXT("", "test");
-
-    UA_Server_addNode(server, (UA_Node*)hierarchicalreferences,
-                      UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_REFERENCES),
-                      UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
+	UA_ReferenceTypeNode *organizes = UA_ReferenceTypeNode_new();
+	copyNames((UA_Node*)organizes, "Organizes");
+	organizes->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "OrganizedBy");
+	organizes->nodeId.identifier.numeric = UA_NS0ID_ORGANIZES;
+	organizes->isAbstract = UA_FALSE;
+	organizes->symmetric  = UA_FALSE;
+	UA_Server_addNode(server, (UA_Node*)organizes, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES), UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
 
 	/* ViewNode */
     UA_ViewNode *viewtest = UA_ViewNode_new();
@@ -167,7 +164,8 @@ START_TEST(ReadSingleAttributeNodeClassWithoutTimestamp)
 				&resp);
 
 		ck_assert_int_eq(-1, resp.value.arrayLength);
-		//ck_assert_int_eq(&UA_TYPES[UA_TYPES_NODECLASS],resp.value.type);
+		ck_assert_int_eq(&UA_TYPES[UA_TYPES_INT32],resp.value.type);
+		ck_assert_int_eq(*(UA_Int32*)resp.value.data,UA_NODECLASS_VARIABLE);
 	}END_TEST
 
 START_TEST(ReadSingleAttributeBrowseNameWithoutTimestamp)
@@ -283,13 +281,13 @@ START_TEST(ReadSingleAttributeWriteMaskWithoutTimestamp)
 
 		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
 				&resp);
-		//UA_UInt32* respval;
-		//respval = (UA_UInt32*) resp.value.data;
-		//UA_VariableNode* compNode = makeCompareSequence();
+
+		UA_UInt32* respval;
+		respval = (UA_UInt32*) resp.value.data;
 
 		ck_assert_int_eq(-1, resp.value.arrayLength);
 		ck_assert_int_eq(&UA_TYPES[UA_TYPES_UINT32], resp.value.type);
-		//ck_assert_int_eq(*(UA_UInt32* )compNode->writeMask,respval);
+		ck_assert_int_eq(0,*respval);
 	}END_TEST
 
 START_TEST(ReadSingleAttributeUserWriteMaskWithoutTimestamp)
@@ -308,8 +306,12 @@ START_TEST(ReadSingleAttributeUserWriteMaskWithoutTimestamp)
 		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
 				&resp);
 
+		UA_UInt32* respval;
+		respval = (UA_UInt32*) resp.value.data;
+
 		ck_assert_int_eq(-1, resp.value.arrayLength);
 		ck_assert_int_eq(&UA_TYPES[UA_TYPES_UINT32], resp.value.type);
+		ck_assert_int_eq(0,*respval);
 	}END_TEST
 
 START_TEST(ReadSingleAttributeIsAbstractWithoutTimestamp)
@@ -322,7 +324,7 @@ START_TEST(ReadSingleAttributeIsAbstractWithoutTimestamp)
 		UA_ReadRequest_init(&rReq);
 		rReq.nodesToRead = UA_ReadValueId_new();
 		rReq.nodesToReadSize = 1;
-		rReq.nodesToRead[0].nodeId.identifier.numeric = UA_NS0ID_HIERARCHICALREFERENCES;
+		rReq.nodesToRead[0].nodeId.identifier.numeric = UA_NS0ID_ORGANIZES;
 		rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_ISABSTRACT;
 
 		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
@@ -330,7 +332,7 @@ START_TEST(ReadSingleAttributeIsAbstractWithoutTimestamp)
 
 		ck_assert_int_eq(-1, resp.value.arrayLength);
 		ck_assert_int_eq(&UA_TYPES[UA_TYPES_BOOLEAN], resp.value.type);
-		ck_assert(*(UA_Boolean* )resp.value.data==UA_TRUE);
+		ck_assert(*(UA_Boolean* )resp.value.data==UA_FALSE);
 	}END_TEST
 
 START_TEST(ReadSingleAttributeSymmetricWithoutTimestamp)
@@ -343,7 +345,7 @@ START_TEST(ReadSingleAttributeSymmetricWithoutTimestamp)
 		UA_ReadRequest_init(&rReq);
 		rReq.nodesToRead = UA_ReadValueId_new();
 		rReq.nodesToReadSize = 1;
-		rReq.nodesToRead[0].nodeId.identifier.numeric = UA_NS0ID_HIERARCHICALREFERENCES;
+		rReq.nodesToRead[0].nodeId.identifier.numeric = UA_NS0ID_ORGANIZES;
 		rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_SYMMETRIC;
 
 		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
@@ -364,19 +366,19 @@ START_TEST(ReadSingleAttributeInverseNameWithoutTimestamp)
 		UA_ReadRequest_init(&rReq);
 		rReq.nodesToRead = UA_ReadValueId_new();
 		rReq.nodesToReadSize = 1;
-		rReq.nodesToRead[0].nodeId.identifier.numeric = UA_NS0ID_HIERARCHICALREFERENCES;
+		rReq.nodesToRead[0].nodeId.identifier.numeric = UA_NS0ID_ORGANIZES;
 		rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_INVERSENAME;
 
 		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
 				&resp);
 
-		/*UA_LocalizedText* respval;
+		UA_LocalizedText* respval;
 		respval = (UA_LocalizedText*) resp.value.data;
-		const UA_LocalizedText comp = UA_LOCALIZEDTEXT("", "test");
-*/
+		const UA_LocalizedText comp = UA_LOCALIZEDTEXT("", "OrganizedBy");
+
 		ck_assert_int_eq(-1, resp.value.arrayLength);
 		ck_assert_int_eq(&UA_TYPES[UA_TYPES_LOCALIZEDTEXT],resp.value.type);
-		/*ck_assert_int_eq(comp.text.length, respval->text.length);
+		ck_assert_int_eq(comp.text.length, respval->text.length);
 		for (int var = 0; var < respval->text.length - 1; ++var) {
 			ck_assert_int_eq(comp.text.data[var], respval->text.data[var]);
 		}
@@ -384,7 +386,7 @@ START_TEST(ReadSingleAttributeInverseNameWithoutTimestamp)
 		for (int var = 0; var < respval->locale.length - 1; ++var) {
 			ck_assert_int_eq(comp.locale.data[var], respval->locale.data[var]);
 		}
-		UA_free(respval);*/
+		UA_free(respval);
 	}END_TEST
 
 START_TEST(ReadSingleAttributeContainsNoLoopsWithoutTimestamp)
@@ -445,13 +447,13 @@ START_TEST(ReadSingleAttributeDataTypeWithoutTimestamp)
 		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
 				&resp);
 
-		//UA_NodeId* respval;
-		//respval = (UA_NodeId*) resp.value.data;
-		//const UA_VariableNode compNode = makeCompareSequence();
-		//const UA_NodeId comp = compNode;
+		UA_NodeId* respval;
+		respval = (UA_NodeId*) resp.value.data;
 
 		ck_assert_int_eq(-1, resp.value.arrayLength);
 		ck_assert_int_eq(&UA_TYPES[UA_TYPES_NODEID], resp.value.type);
+		ck_assert_int_eq(respval->namespaceIndex,0);
+		ck_assert_int_eq(respval->identifier.numeric,UA_NS0ID_INT32);
 	}END_TEST
 
 START_TEST(ReadSingleAttributeValueRankWithoutTimestamp)
@@ -494,6 +496,7 @@ START_TEST(ReadSingleAttributeArrayDimensionsWithoutTimestamp)
 
 		ck_assert_int_eq(-1, resp.value.arrayLength);
 		ck_assert_int_eq(&UA_TYPES[UA_TYPES_INT32], resp.value.type);
+		ck_assert_int_eq((UA_Int32*)resp.value.data,0);
 	}END_TEST
 
 START_TEST(ReadSingleAttributeAccessLevelWithoutTimestamp)
@@ -556,13 +559,15 @@ START_TEST(ReadSingleAttributeMinimumSamplingIntervalWithoutTimestamp)
 		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
 				&resp);
 
-		//UA_Double* respval;
-		//respval = (UA_Double*) resp.value.data;
-		//UA_VariableNode* compNode = makeCompareSequence();
+		UA_Double* respval;
+		respval = (UA_Double*) resp.value.data;
+		UA_VariableNode *compNode = makeCompareSequence();
+		UA_Double comp;
+		comp = (UA_Double) compNode->minimumSamplingInterval;
 
 		ck_assert_int_eq(-1, resp.value.arrayLength);
 		ck_assert_int_eq(&UA_TYPES[UA_TYPES_DOUBLE], resp.value.type);
-		//ck_assert_int_eq(compNode->minimumSamplingInterval,respval);
+		ck_assert(*respval == comp);
 	}END_TEST
 
 START_TEST(ReadSingleAttributeHistorizingWithoutTimestamp)
@@ -596,13 +601,14 @@ START_TEST(ReadSingleAttributeExecutableWithoutTimestamp)
 		UA_ReadRequest_init(&rReq);
 		rReq.nodesToRead = UA_ReadValueId_new();
 		rReq.nodesToReadSize = 1;
-		rReq.nodesToRead[0].nodeId = UA_NODEID_STRING(1, "the.answer");
+		rReq.nodesToRead[0].nodeId.identifier.numeric = UA_NS0ID_METHODNODE;
 		rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_EXECUTABLE;
 
 		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0], &resp);
 
 		ck_assert_int_eq(-1, resp.value.arrayLength);
 		ck_assert_int_eq(&UA_TYPES[UA_TYPES_BOOLEAN], resp.value.type);
+		ck_assert(*(UA_Boolean*)resp.value.data==UA_FALSE);
 	}END_TEST
 
 START_TEST(ReadSingleAttributeUserExecutableWithoutTimestamp)
@@ -615,7 +621,7 @@ START_TEST(ReadSingleAttributeUserExecutableWithoutTimestamp)
 		UA_ReadRequest_init(&rReq);
 		rReq.nodesToRead = UA_ReadValueId_new();
 		rReq.nodesToReadSize = 1;
-		rReq.nodesToRead[0].nodeId = UA_NODEID_STRING(1, "the.answer");
+		rReq.nodesToRead[0].nodeId.identifier.numeric = UA_NS0ID_METHODNODE;
 		rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_USEREXECUTABLE;
 
 		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
@@ -623,6 +629,353 @@ START_TEST(ReadSingleAttributeUserExecutableWithoutTimestamp)
 
 		ck_assert_int_eq(-1, resp.value.arrayLength);
 		ck_assert_int_eq(&UA_TYPES[UA_TYPES_BOOLEAN], resp.value.type);
+		ck_assert(*(UA_Boolean*)resp.value.data==UA_FALSE);
+	}END_TEST
+
+
+/* Tests for writeValue method */
+
+START_TEST(WriteSingleAttributeNodeId)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_NODEID;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeNodeclass)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_NODECLASS;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeBrowseName)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_BROWSENAME;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeDisplayName)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_DISPLAYNAME;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeDescription)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_DESCRIPTION;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeWriteMask)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_WRITEMASK;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeUserWriteMask)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_USERWRITEMASK;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeIsAbstract)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_ISABSTRACT;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeSymmetric)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_SYMMETRIC;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeInverseName)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_INVERSENAME;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeContainsNoLoops)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_CONTAINSNOLOOPS;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeEventNotifier)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_EVENTNOTIFIER;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+
+START_TEST(WriteSingleAttributeValue)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		UA_Variant *myIntegerVariant = UA_Variant_new();
+		UA_Int32 myInteger = 20;
+		UA_Variant_setScalarCopy(myIntegerVariant, &myInteger,
+						&UA_TYPES[UA_TYPES_INT32]);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_VALUE;
+		wValue.value.hasValue = UA_TRUE;
+		wValue.value.value = *myIntegerVariant;
+		UA_StatusCode retval = writeValue(server, &wValue);
+
+		UA_DataValue resp;
+		UA_DataValue_init(&resp);
+		UA_ReadRequest rReq;
+		UA_ReadRequest_init(&rReq);
+		rReq.nodesToRead = UA_ReadValueId_new();
+		rReq.nodesToReadSize = 1;
+		rReq.nodesToRead[0].nodeId = UA_NODEID_STRING(1, "the.answer");
+		rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_VALUE;
+
+		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
+					&resp);
+
+		ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
+		ck_assert(wValue.value.hasValue);
+		const UA_Node *node = UA_NodeStore_get(server->nodestore, &wValue.nodeId);
+		ck_assert_int_eq(node->nodeClass, UA_NODECLASS_VARIABLE);
+		const UA_VariableNode *vn = (const UA_VariableNode*)node;
+		const UA_Variant *oldV = &vn->value.variant;
+		ck_assert_int_eq(&oldV->type->typeId, &wValue.value.value.type->typeId);
+
+		ck_assert_int_eq(20, *(UA_Int32* )resp.value.data);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeDataType)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_DATATYPE;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeValueRank)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_VALUERANK;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeArrayDimensions)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_ARRAYDIMENSIONS;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeAccessLevel)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_ACCESSLEVEL;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeUserAccessLevel)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_USERACCESSLEVEL;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeMinimumSamplingInterval)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeHistorizing)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_HISTORIZING;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeExecutable)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_EXECUTABLE;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeUserExecutable)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_USEREXECUTABLE;
+		wValue.value.hasValue = UA_TRUE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+	}END_TEST
+
+START_TEST(WriteSingleAttributeNoValue)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_WriteValue wValue;
+		UA_WriteValue_init(&wValue);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
+		wValue.attributeId = UA_ATTRIBUTEID_USEREXECUTABLE;
+		wValue.value.hasValue = UA_FALSE;
+		UA_StatusCode retval = writeValue(server, &wValue);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADTYPEMISMATCH);
+	}END_TEST
+
+START_TEST(numericRange)
+	{
+		//UA_Server *server = makeTestSequence();
+
+        UA_NumericRange range;
+		const UA_String str = (UA_String){9, (UA_Byte*)"1:2,0:3,5"};
+		UA_StatusCode retval = parse_numericrange(str, &range);
+		ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
+		ck_assert_int_eq(range.dimensionsSize,3);
+		ck_assert_int_eq(range.dimensions[0].min,1);
+		ck_assert_int_eq(range.dimensions[0].max,2);
+		ck_assert_int_eq(range.dimensions[1].min,0);
+		ck_assert_int_eq(range.dimensions[1].max,3);
+		ck_assert_int_eq(range.dimensions[2].min,5);
+		ck_assert_int_eq(range.dimensions[2].max,5);
 	}END_TEST
 
 static Suite * testSuite_services_attributes(void) {
@@ -675,6 +1028,38 @@ static Suite * testSuite_services_attributes(void) {
 			ReadSingleAttributeUserExecutableWithoutTimestamp);
 
 	suite_add_tcase(s, tc_readSingleAttributes);
+
+	TCase *tc_writeSingleAttributes = tcase_create("writeSingleAttributes");
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeNodeId);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeNodeclass);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeBrowseName);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeDisplayName);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeDescription);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeWriteMask);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeUserWriteMask);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeIsAbstract);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeSymmetric);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeInverseName);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeContainsNoLoops);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeEventNotifier);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeValue);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeDataType);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeValueRank);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeArrayDimensions);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeAccessLevel);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeUserAccessLevel);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeMinimumSamplingInterval);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeHistorizing);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeExecutable);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeUserExecutable);
+	tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeNoValue);
+
+	suite_add_tcase(s, tc_writeSingleAttributes);
+
+	TCase *tc_parseNumericRange = tcase_create("parseNumericRange");
+	tcase_add_test(tc_parseNumericRange, numericRange);
+	suite_add_tcase(s, tc_parseNumericRange);
+
 	return s;
 }
 

+ 0 - 3
tools/amalgamate.py

@@ -55,9 +55,6 @@ extern "C" {
 if not is_c:
     for inc in includes:
         file.write(u"#include " + inc + "\n")
-    file.write(u'''#ifndef UA_AMALGAMATE
-# define UA_AMALGAMATE
-#endif\n\n''')
 else:
     file.write(u'''#ifndef UA_DYNAMIC_LINKING
 # define UA_DYNAMIC_LINKING