Bläddra i källkod

Merge branch 'master' into dev

Julius Pfrommer 9 år sedan
förälder
incheckning
3f317baa42

+ 11 - 0
src/server/ua_services.h

@@ -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;
 }
 

+ 10 - 1
tools/pyUANamespace/ua_node_types.py

@@ -657,12 +657,21 @@ class opcua_node_t:
       if parent[1].referenceType() != None:
         code.append("// Referencing node found and declared as parent: " + str(parent[0].id()) + "/" + str(parent[0].__node_browseName__) + " using " + str(parent[1].referenceType().id()) + "/" + str(parent[1].referenceType().__node_browseName__))
         code.append("UA_Server_addNode(server, (UA_Node*) " + self.getCodePrintableID() + ", " + codegen.getCreateExpandedNodeIDMacro(parent[0]) + ", " + codegen.getCreateNodeIDMacro(parent[1].referenceType()) + ");")
+    
     # Otherwise use the "Bootstrapping" method and we will get registered
     # with other nodes later.
     else:
       code.append("// Parent node does not exist yet. This node will be bootstrapped and linked later.")
       code.append("UA_NodeStore_insert(server->nodestore, (UA_Node*) " + self.getCodePrintableID() + ", UA_NULL);")
-
+    # Note: getFirstParentNode will return [parentNode, referenceToChild]
+    (parentNode, parentRef) = self.getFirstParentNode()
+    if not (parentNode in unPrintedNodes) and (parentNode != None):
+      if parentRef.referenceType() != None:
+        code.append("// Referencing node found and declared as parent: " + str(parentNode .id()) + "/" + str(parentNode .__node_browseName__) + " using " + str(parentRef.referenceType().id()) + "/" + str(parentRef.referenceType().__node_browseName__))
+        code.append("UA_Server_addNode(server, (UA_Node*) " + self.getCodePrintableID() + ", " + codegen.getCreateExpandedNodeIDMacro(parentNode ) + ", " + codegen.getCreateNodeIDMacro(parentRef.referenceType()) + ");")
+        # Parent to child reference is added by the server, do not reprint that reference
+        if parentRef in unPrintedReferences:
+          unPrintedReferences.remove(parentRef)
     # Try to print all references to nodes that already exist
     # Note: we know the reference types exist, because the namespace class made sure they were
     #       the first ones being printed