Просмотр исходного кода

server_historical_update: Add test for the historical update.

Peter Rustler лет назад: 5
Родитель
Сommit
9b29a27e85
2 измененных файлов с 328 добавлено и 1 удалено
  1. 309 1
      tests/server/check_server_historical_data.c
  2. 19 0
      tests/server/historical_read_test_data.h

+ 309 - 1
tests/server/check_server_historical_data.c

@@ -43,6 +43,8 @@ static UA_NodeId parentNodeId;
 static UA_NodeId parentReferenceNodeId;
 static UA_NodeId outNodeId;
 
+static UA_DateTime *testDataSorted;
+
 static void serverMutexLock(void) {
     if (!(MUTEX_LOCK(serverMutex))) {
         fprintf(stderr, "Mutex cannot be locked.\n");
@@ -96,7 +98,7 @@ setup(void)
     attr.description = UA_LOCALIZEDTEXT("en-US","the answer");
     attr.displayName = UA_LOCALIZEDTEXT("en-US","the answer");
     attr.dataType = UA_TYPES[UA_TYPES_UINT32].typeId;
-    attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE | UA_ACCESSLEVELMASK_HISTORYREAD;
+    attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE | UA_ACCESSLEVELMASK_HISTORYREAD | UA_ACCESSLEVELMASK_HISTORYWRITE;
     attr.historizing = true;
 
     /* Add the variable node to the information model */
@@ -168,6 +170,26 @@ setUInt32(UA_Client *thisClient, UA_NodeId node, UA_UInt32 value)
     return UA_Client_writeValueAttribute(thisClient, node, &variant);
 }
 
+static UA_DateTime* sortDateTimes(UA_DateTime *data) {
+    size_t count = 0;
+    while(data[count++]);
+    UA_DateTime* ret;
+    if (UA_Array_copy(data, count, (void**)&ret, &UA_TYPES[UA_TYPES_DATETIME]) != UA_STATUSCODE_GOOD)
+        return NULL;
+    --count;
+    // sort it
+    for (size_t i = 1; i < count; i++) {
+       for (size_t j = 0; j < count - i; j++) {
+           if (ret[j] > ret[j+1]) {
+               UA_DateTime tmp = ret[j];
+               ret[j] = ret[j+1];
+               ret[j+1] = tmp;
+           }
+       }
+    }
+    return ret;
+}
+
 static void
 printTimestamp(UA_DateTime timestamp)
 {
@@ -452,6 +474,282 @@ testHistoricalDataBackend(size_t maxResponseSize)
     return retval;
 }
 
+void
+Service_HistoryUpdate(UA_Server *server, UA_Session *session,
+                      const UA_HistoryUpdateRequest *request,
+                      UA_HistoryUpdateResponse *response);
+
+static UA_StatusCode
+deleteHistory(UA_DateTime start,
+              UA_DateTime end)
+{
+    UA_DeleteRawModifiedDetails *details = UA_DeleteRawModifiedDetails_new();
+    details->startTime = start;
+    details->endTime = end;
+    details->isDeleteModified = false;
+    UA_NodeId_copy(&outNodeId, &details->nodeId);
+
+    UA_HistoryUpdateRequest request;
+    UA_HistoryUpdateRequest_init(&request);
+    request.historyUpdateDetailsSize = 1;
+    request.historyUpdateDetails = UA_ExtensionObject_new();
+    UA_ExtensionObject_init(request.historyUpdateDetails);
+
+    request.historyUpdateDetails[0].encoding = UA_EXTENSIONOBJECT_DECODED;
+    request.historyUpdateDetails[0].content.decoded.type = &UA_TYPES[UA_TYPES_DELETERAWMODIFIEDDETAILS];
+    request.historyUpdateDetails[0].content.decoded.data = details;
+
+    UA_HistoryUpdateResponse response;
+    UA_HistoryUpdateResponse_init(&response);
+    Service_HistoryUpdate(server, &server->adminSession, &request, &response);
+    UA_HistoryUpdateRequest_deleteMembers(&request);
+    UA_StatusCode ret = UA_STATUSCODE_GOOD;
+    if (response.responseHeader.serviceResult != UA_STATUSCODE_GOOD)
+        ret = response.responseHeader.serviceResult;
+    else if (response.resultsSize != 1)
+        ret = UA_STATUSCODE_BADUNEXPECTEDERROR;
+    else if (response.results[0].statusCode != UA_STATUSCODE_GOOD)
+        ret = response.results[0].statusCode;
+    else if (response.results[0].operationResultsSize != 0)
+        ret = UA_STATUSCODE_BADUNEXPECTEDERROR;
+
+    UA_HistoryUpdateResponse_deleteMembers(&response);
+    return ret;
+}
+
+static UA_StatusCode
+updateHistory(UA_PerformUpdateType updateType, UA_DateTime *updateData, UA_StatusCode ** operationResults, size_t *operationResultsSize)
+{
+    UA_UpdateDataDetails *details = UA_UpdateDataDetails_new();
+    details->performInsertReplace = updateType;
+    UA_NodeId_copy(&outNodeId, &details->nodeId);
+    int updateDataSize = -1;
+    while(updateData[++updateDataSize]);
+    fprintf(stderr, "updateHistory for %d values.\n", updateDataSize);
+    details->updateValuesSize = (size_t)updateDataSize;
+    details->updateValues = (UA_DataValue*)UA_Array_new(details->updateValuesSize, &UA_TYPES[UA_TYPES_DATAVALUE]);
+    for (size_t i = 0; i < details->updateValuesSize; ++i) {
+        UA_DataValue_init(&details->updateValues[i]);
+        details->updateValues[i].hasValue = true;
+        UA_Int64 d = updateType;
+        UA_Variant_setScalarCopy(&details->updateValues[i].value, &d, &UA_TYPES[UA_TYPES_INT64]);
+        details->updateValues[i].hasSourceTimestamp = true;
+        details->updateValues[i].sourceTimestamp = updateData[i];
+        details->updateValues[i].hasServerTimestamp = true;
+        details->updateValues[i].serverTimestamp = updateData[i];
+        details->updateValues[i].hasStatus = true;
+        details->updateValues[i].status = UA_STATUSCODE_GOOD;
+    }
+
+    UA_HistoryUpdateRequest request;
+    UA_HistoryUpdateRequest_init(&request);
+    request.historyUpdateDetailsSize = 1;
+    request.historyUpdateDetails = UA_ExtensionObject_new();
+    UA_ExtensionObject_init(request.historyUpdateDetails);
+
+    request.historyUpdateDetails[0].encoding = UA_EXTENSIONOBJECT_DECODED;
+    request.historyUpdateDetails[0].content.decoded.type = &UA_TYPES[UA_TYPES_UPDATEDATADETAILS];
+    request.historyUpdateDetails[0].content.decoded.data = details;
+
+    UA_HistoryUpdateResponse response;
+    UA_HistoryUpdateResponse_init(&response);
+    Service_HistoryUpdate(server, &server->adminSession, &request, &response);
+    UA_HistoryUpdateRequest_deleteMembers(&request);
+    UA_StatusCode ret = UA_STATUSCODE_GOOD;
+    if (response.responseHeader.serviceResult != UA_STATUSCODE_GOOD)
+        ret = response.responseHeader.serviceResult;
+    else if (response.resultsSize != 1)
+        ret = UA_STATUSCODE_BADUNEXPECTEDERROR;
+    else if (response.results[0].statusCode != UA_STATUSCODE_GOOD)
+        ret = response.results[0].statusCode;
+    else if (response.results[0].operationResultsSize != (size_t)updateDataSize)
+        ret = UA_STATUSCODE_BADUNEXPECTEDERROR;
+    else {
+        if (operationResults) {
+            *operationResultsSize = response.results[0].operationResultsSize;
+            ret = UA_Array_copy(response.results[0].operationResults, *operationResultsSize, (void**)operationResults, &UA_TYPES[UA_TYPES_STATUSCODE]);
+        } else {
+            for (size_t i = 0; i < response.results[0].operationResultsSize; ++i) {
+                if (response.results[0].operationResults[i] != UA_STATUSCODE_GOOD) {
+                    ret = response.results[0].operationResults[i];
+                    break;
+                }
+            }
+        }
+    }
+    UA_HistoryUpdateResponse_deleteMembers(&response);
+    return ret;
+}
+
+static void
+testResult(UA_DateTime *resultData, UA_HistoryData * historyData) {
+
+    // request
+    UA_HistoryReadResponse localResponse;
+    UA_HistoryReadResponse_init(&localResponse);
+    requestHistory(TIMESTAMP_FIRST, TIMESTAMP_LAST, &localResponse, 0, false, NULL);
+
+    // test the response
+    ck_assert_str_eq(UA_StatusCode_name(localResponse.responseHeader.serviceResult), UA_StatusCode_name(UA_STATUSCODE_GOOD));
+    ck_assert_uint_eq(localResponse.resultsSize, 1);
+    ck_assert_str_eq(UA_StatusCode_name(localResponse.results[0].statusCode), UA_StatusCode_name(UA_STATUSCODE_GOOD));
+    ck_assert_uint_eq(localResponse.results[0].historyData.encoding, UA_EXTENSIONOBJECT_DECODED);
+    ck_assert(localResponse.results[0].historyData.content.decoded.type == &UA_TYPES[UA_TYPES_HISTORYDATA]);
+    UA_HistoryData * data = (UA_HistoryData *)localResponse.results[0].historyData.content.decoded.data;
+    if (historyData)
+        UA_HistoryData_copy(data, historyData);
+    for (size_t j = 0; j < data->dataValuesSize; ++j) {
+        ck_assert(resultData[j] != 0);
+        ck_assert_uint_eq(data->dataValues[j].hasSourceTimestamp, true);
+        ck_assert_uint_eq(data->dataValues[j].sourceTimestamp, resultData[j]);
+    }
+    UA_HistoryReadResponse_deleteMembers(&localResponse);
+}
+
+START_TEST(Server_HistorizingUpdateDelete)
+{
+    UA_HistoryDataBackend backend = UA_HistoryDataBackend_Memory(1, 1);
+    UA_HistorizingNodeIdSettings setting;
+    setting.historizingBackend = backend;
+    setting.maxHistoryDataResponseSize = 1000;
+    setting.historizingUpdateStrategy = UA_HISTORIZINGUPDATESTRATEGY_USER;
+    serverMutexLock();
+    UA_StatusCode ret = gathering->registerNodeId(server, gathering->context, &outNodeId, setting);
+    serverMutexUnlock();
+    ck_assert_str_eq(UA_StatusCode_name(ret), UA_StatusCode_name(UA_STATUSCODE_GOOD));
+
+    // fill backend
+    ck_assert_uint_eq(fillHistoricalDataBackend(backend), true);
+
+    // delete some values
+    ck_assert_str_eq(UA_StatusCode_name(deleteHistory(DELETE_START_TIME, DELETE_STOP_TIME)),
+                     UA_StatusCode_name(UA_STATUSCODE_GOOD));
+
+    testResult(testDataAfterDelete, NULL);
+
+    UA_HistoryDataBackend_Memory_deleteMembers(&setting.historizingBackend);
+}
+END_TEST
+
+START_TEST(Server_HistorizingUpdateInsert)
+{
+    UA_HistoryDataBackend backend = UA_HistoryDataBackend_Memory(1, 1);
+    UA_HistorizingNodeIdSettings setting;
+    setting.historizingBackend = backend;
+    setting.maxHistoryDataResponseSize = 1000;
+    setting.historizingUpdateStrategy = UA_HISTORIZINGUPDATESTRATEGY_USER;
+    serverMutexLock();
+    UA_StatusCode ret = gathering->registerNodeId(server, gathering->context, &outNodeId, setting);
+    serverMutexUnlock();
+    ck_assert_str_eq(UA_StatusCode_name(ret), UA_StatusCode_name(UA_STATUSCODE_GOOD));
+
+    // fill backend with insert
+    ck_assert_str_eq(UA_StatusCode_name(updateHistory(UA_PERFORMUPDATETYPE_INSERT, testData, NULL, NULL))
+                                        , UA_StatusCode_name(UA_STATUSCODE_GOOD));
+
+    UA_HistoryData data;
+    UA_HistoryData_init(&data);
+
+    testResult(testDataSorted, &data);
+
+    for (size_t i = 0; i < data.dataValuesSize; ++i) {
+        ck_assert_uint_eq(data.dataValues[i].hasValue, true);
+        ck_assert(data.dataValues[i].value.type == &UA_TYPES[UA_TYPES_INT64]);
+        ck_assert_uint_eq(*((UA_Int64*)data.dataValues[i].value.data), UA_PERFORMUPDATETYPE_INSERT);
+    }
+
+    UA_HistoryData_deleteMembers(&data);
+    UA_HistoryDataBackend_Memory_deleteMembers(&setting.historizingBackend);
+}
+END_TEST
+
+START_TEST(Server_HistorizingUpdateReplace)
+{
+    UA_HistoryDataBackend backend = UA_HistoryDataBackend_Memory(1, 1);
+    UA_HistorizingNodeIdSettings setting;
+    setting.historizingBackend = backend;
+    setting.maxHistoryDataResponseSize = 1000;
+    setting.historizingUpdateStrategy = UA_HISTORIZINGUPDATESTRATEGY_USER;
+    serverMutexLock();
+    UA_StatusCode ret = gathering->registerNodeId(server, gathering->context, &outNodeId, setting);
+    serverMutexUnlock();
+    ck_assert_str_eq(UA_StatusCode_name(ret), UA_StatusCode_name(UA_STATUSCODE_GOOD));
+
+    // fill backend with insert
+    ck_assert_str_eq(UA_StatusCode_name(updateHistory(UA_PERFORMUPDATETYPE_INSERT, testData, NULL, NULL))
+                                        , UA_StatusCode_name(UA_STATUSCODE_GOOD));
+
+    // replace all
+    ck_assert_str_eq(UA_StatusCode_name(updateHistory(UA_PERFORMUPDATETYPE_REPLACE, testData, NULL, NULL))
+                                        , UA_StatusCode_name(UA_STATUSCODE_GOOD));
+
+    UA_HistoryData data;
+    UA_HistoryData_init(&data);
+
+    testResult(testDataSorted, &data);
+
+    for (size_t i = 0; i < data.dataValuesSize; ++i) {
+        ck_assert_uint_eq(data.dataValues[i].hasValue, true);
+        ck_assert(data.dataValues[i].value.type == &UA_TYPES[UA_TYPES_INT64]);
+        ck_assert_uint_eq(*((UA_Int64*)data.dataValues[i].value.data), UA_PERFORMUPDATETYPE_REPLACE);
+    }
+
+    UA_HistoryData_deleteMembers(&data);
+    UA_HistoryDataBackend_Memory_deleteMembers(&setting.historizingBackend);
+}
+END_TEST
+
+START_TEST(Server_HistorizingUpdateUpdate)
+{
+    UA_HistoryDataBackend backend = UA_HistoryDataBackend_Memory(1, 1);
+    UA_HistorizingNodeIdSettings setting;
+    setting.historizingBackend = backend;
+    setting.maxHistoryDataResponseSize = 1000;
+    setting.historizingUpdateStrategy = UA_HISTORIZINGUPDATESTRATEGY_USER;
+    serverMutexLock();
+    UA_StatusCode ret = gathering->registerNodeId(server, gathering->context, &outNodeId, setting);
+    serverMutexUnlock();
+    ck_assert_str_eq(UA_StatusCode_name(ret), UA_StatusCode_name(UA_STATUSCODE_GOOD));
+
+    // fill backend with insert
+    ck_assert_str_eq(UA_StatusCode_name(updateHistory(UA_PERFORMUPDATETYPE_INSERT, testData, NULL, NULL))
+                                        , UA_StatusCode_name(UA_STATUSCODE_GOOD));
+
+    testResult(testDataSorted, NULL);
+
+    // delete some values
+    ck_assert_str_eq(UA_StatusCode_name(deleteHistory(DELETE_START_TIME, DELETE_STOP_TIME)),
+                     UA_StatusCode_name(UA_STATUSCODE_GOOD));
+
+    testResult(testDataAfterDelete, NULL);
+
+    // update all and insert some
+    UA_StatusCode *result;
+    size_t resultSize = 0;
+    ck_assert_str_eq(UA_StatusCode_name(updateHistory(UA_PERFORMUPDATETYPE_UPDATE, testDataSorted, &result, &resultSize))
+                                        , UA_StatusCode_name(UA_STATUSCODE_GOOD));
+
+    for (size_t i = 0; i < resultSize; ++i) {
+        ck_assert_str_eq(UA_StatusCode_name(result[i]), UA_StatusCode_name(testDataUpdateResult[i]));
+    }
+    UA_Array_delete(result, resultSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
+
+    UA_HistoryData data;
+    UA_HistoryData_init(&data);
+
+    testResult(testDataSorted, &data);
+
+    for (size_t i = 0; i < data.dataValuesSize; ++i) {
+        ck_assert_uint_eq(data.dataValues[i].hasValue, true);
+        ck_assert(data.dataValues[i].value.type == &UA_TYPES[UA_TYPES_INT64]);
+        ck_assert_uint_eq(*((UA_Int64*)data.dataValues[i].value.data), UA_PERFORMUPDATETYPE_UPDATE);
+    }
+
+    UA_HistoryData_deleteMembers(&data);
+    UA_HistoryDataBackend_Memory_deleteMembers(&setting.historizingBackend);
+}
+END_TEST
+
 START_TEST(Server_HistorizingStrategyUser)
 {
     // set a data backend
@@ -695,6 +993,10 @@ static Suite* testSuite_Client(void)
     tcase_add_test(tc_server, Server_HistorizingStrategyUser);
     tcase_add_test(tc_server, Server_HistorizingStrategyValueSet);
     tcase_add_test(tc_server, Server_HistorizingBackendMemory);
+    tcase_add_test(tc_server, Server_HistorizingUpdateDelete);
+    tcase_add_test(tc_server, Server_HistorizingUpdateInsert);
+    tcase_add_test(tc_server, Server_HistorizingUpdateReplace);
+    tcase_add_test(tc_server, Server_HistorizingUpdateUpdate);
 #endif /* UA_ENABLE_HISTORIZING */
     suite_add_tcase(s, tc_server);
 
@@ -703,11 +1005,17 @@ static Suite* testSuite_Client(void)
 
 int main(void)
 {
+#ifdef UA_ENABLE_HISTORIZING
+    testDataSorted = sortDateTimes(testData);
+#endif /* UA_ENABLE_HISTORIZING */
     Suite *s = testSuite_Client();
     SRunner *sr = srunner_create(s);
     srunner_set_fork_status(sr, CK_NOFORK);
     srunner_run_all(sr,CK_NORMAL);
     int number_failed = srunner_ntests_failed(sr);
     srunner_free(sr);
+#ifdef UA_ENABLE_HISTORIZING
+    UA_free(testDataSorted);
+#endif /* UA_ENABLE_HISTORIZING */
     return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
 }

+ 19 - 0
tests/server/historical_read_test_data.h

@@ -47,6 +47,25 @@ static UA_DateTime testData[] = {
     TIMESTAMP_5_05,
     0 // last element
 };
+
+#define DELETE_START_TIME TIMESTAMP_5_03
+#define DELETE_STOP_TIME  TIMESTAMP_5_06
+
+static UA_DateTime testDataAfterDelete[] = {
+    TIMESTAMP_5_00,
+    TIMESTAMP_5_02,
+    TIMESTAMP_5_06,
+    0 // last element
+};
+
+static UA_StatusCode testDataUpdateResult[] = {
+    UA_STATUSCODE_GOODENTRYREPLACED,
+    UA_STATUSCODE_GOODENTRYREPLACED,
+    UA_STATUSCODE_GOODENTRYINSERTED,
+    UA_STATUSCODE_GOODENTRYINSERTED,
+    UA_STATUSCODE_GOODENTRYREPLACED
+};
+
 static testTuple testRequests[] =
 {
     { TIMESTAMP_5_00,