/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2019 (c) basysKom GmbH (Author: Frank Meerkötter) */ #include #include #include #include "server/ua_server_internal.h" #include "server/ua_services.h" #include #include #include #include "check.h" static UA_Server *server = NULL; static UA_StatusCode methodCallback(UA_Server *serverArg, const UA_NodeId *sessionId, void *sessionHandle, const UA_NodeId *methodId, void *methodContext, const UA_NodeId *objectId, void *objectContext, size_t inputSize, const UA_Variant *input, size_t outputSize, UA_Variant *output) { return UA_STATUSCODE_GOOD; } static void setup(void) { server = UA_Server_new(); UA_ServerConfig_setDefault(UA_Server_getConfig(server)); UA_MethodAttributes noFpAttr = UA_MethodAttributes_default; noFpAttr.description = UA_LOCALIZEDTEXT("en-US","No function pointer attached"); noFpAttr.displayName = UA_LOCALIZEDTEXT("en-US","No function pointer attached"); noFpAttr.executable = true; noFpAttr.userExecutable = true; UA_Server_addMethodNode(server, UA_NODEID_STRING(1, "nofunctionpointer"), UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_HASORDEREDCOMPONENT), UA_QUALIFIEDNAME(1, "No function pointer"), noFpAttr, NULL, // no callback 0, NULL, 0, NULL, NULL, NULL); UA_MethodAttributes nonExecAttr = UA_MethodAttributes_default; nonExecAttr.description = UA_LOCALIZEDTEXT("en-US","Not executable"); nonExecAttr.displayName = UA_LOCALIZEDTEXT("en-US","Not executable"); nonExecAttr.executable = false; nonExecAttr.userExecutable = true; UA_Server_addMethodNode(server, UA_NODEID_STRING(1, "nonexec"), UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_HASORDEREDCOMPONENT), UA_QUALIFIEDNAME(1, "Not executable"), nonExecAttr, &methodCallback, 0, NULL, 0, NULL, NULL, NULL); } static void teardown(void) { UA_Server_delete(server); } START_TEST(callUnknownMethod) { const UA_UInt32 UA_NS0ID_UNKNOWN_METHOD = 60000; UA_CallMethodRequest callMethodRequest; UA_CallMethodRequest_init(&callMethodRequest); callMethodRequest.methodId = UA_NODEID_NUMERIC(0, UA_NS0ID_UNKNOWN_METHOD); UA_CallMethodResult result; UA_CallMethodResult_init(&result); result = UA_Server_call(server, &callMethodRequest); ck_assert_int_eq(result.statusCode, UA_STATUSCODE_BADNODEIDUNKNOWN); } END_TEST START_TEST(callKnownMethodOnUnknownObject) { const UA_UInt32 UA_NS0ID_UNKNOWN_OBJECT = 60000; UA_CallMethodRequest callMethodRequest; UA_CallMethodRequest_init(&callMethodRequest); callMethodRequest.methodId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_REQUESTSERVERSTATECHANGE); callMethodRequest.objectId = UA_NODEID_NUMERIC(0, UA_NS0ID_UNKNOWN_OBJECT); UA_CallMethodResult result; UA_CallMethodResult_init(&result); result = UA_Server_call(server, &callMethodRequest); ck_assert_int_eq(result.statusCode, UA_STATUSCODE_BADNODEIDUNKNOWN); } END_TEST START_TEST(callMethodAndObjectExistsButMethodHasWrongNodeClass) { UA_CallMethodRequest callMethodRequest; UA_CallMethodRequest_init(&callMethodRequest); callMethodRequest.methodId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS); // not a method callMethodRequest.objectId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER); UA_CallMethodResult result; UA_CallMethodResult_init(&result); result = UA_Server_call(server, &callMethodRequest); ck_assert_int_eq(result.statusCode, UA_STATUSCODE_BADNODECLASSINVALID); } END_TEST START_TEST(callMethodOnUnrelatedObject) { /* Minimal nodeset does not add any method nodes we may call here */ #ifdef UA_GENERATED_NAMESPACE_ZERO UA_CallMethodRequest callMethodRequest; UA_CallMethodRequest_init(&callMethodRequest); callMethodRequest.methodId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_GETMONITOREDITEMS); callMethodRequest.objectId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER); // not connected via hasComponent UA_CallMethodResult result; UA_CallMethodResult_init(&result); result = UA_Server_call(server, &callMethodRequest); ck_assert_int_eq(result.statusCode, UA_STATUSCODE_BADMETHODINVALID); #endif } END_TEST START_TEST(callMethodAndObjectExistsButNoFunctionPointerAttached) { UA_CallMethodRequest callMethodRequest; UA_CallMethodRequest_init(&callMethodRequest); callMethodRequest.methodId = UA_NODEID_STRING(1, "nofunctionpointer"); callMethodRequest.objectId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER); UA_CallMethodResult result; UA_CallMethodResult_init(&result); result = UA_Server_call(server, &callMethodRequest); ck_assert_int_eq(result.statusCode, UA_STATUSCODE_BADINTERNALERROR); } END_TEST START_TEST(callMethodNonExecutable) { UA_CallMethodRequest callMethodRequest; UA_CallMethodRequest_init(&callMethodRequest); callMethodRequest.methodId = UA_NODEID_STRING(1, "nonexec"); callMethodRequest.objectId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER); UA_CallMethodResult result; UA_CallMethodResult_init(&result); result = UA_Server_call(server, &callMethodRequest); ck_assert_int_eq(result.statusCode, UA_STATUSCODE_BADNOTEXECUTABLE); } END_TEST START_TEST(callMethodWithMissingArguments) { /* Minimal nodeset does not add any method nodes we may call here */ #ifdef UA_GENERATED_NAMESPACE_ZERO UA_CallMethodRequest callMethodRequest; UA_CallMethodRequest_init(&callMethodRequest); callMethodRequest.methodId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_GETMONITOREDITEMS); callMethodRequest.objectId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER); UA_CallMethodResult result; UA_CallMethodResult_init(&result); result = UA_Server_call(server, &callMethodRequest); ck_assert_int_eq(result.statusCode, UA_STATUSCODE_BADARGUMENTSMISSING); #endif } END_TEST START_TEST(callMethodWithTooManyArguments) { /* Minimal nodeset does not add any method nodes we may call here */ #ifdef UA_GENERATED_NAMESPACE_ZERO UA_Variant inputArguments[2]; UA_Variant_init(&inputArguments[0]); UA_Variant_init(&inputArguments[1]); UA_CallMethodRequest callMethodRequest; UA_CallMethodRequest_init(&callMethodRequest); callMethodRequest.inputArgumentsSize = 2; // 1 would be correct callMethodRequest.inputArguments = (UA_Variant*)&inputArguments; callMethodRequest.methodId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_GETMONITOREDITEMS); callMethodRequest.objectId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER); UA_CallMethodResult result; UA_CallMethodResult_init(&result); result = UA_Server_call(server, &callMethodRequest); ck_assert_int_eq(result.statusCode, UA_STATUSCODE_BADTOOMANYARGUMENTS); #endif } END_TEST START_TEST(callMethodWithWronglyTypedArguments) { /* Minimal nodeset does not add any method nodes we may call here */ #ifdef UA_GENERATED_NAMESPACE_ZERO UA_Variant inputArgument; UA_Variant_init(&inputArgument); UA_Double wrongType = 1.0; UA_Variant_setScalar(&inputArgument, &wrongType, &UA_TYPES[UA_TYPES_DOUBLE]); // UA_UInt32 would be correct UA_CallMethodRequest callMethodRequest; UA_CallMethodRequest_init(&callMethodRequest); callMethodRequest.inputArgumentsSize = 1; callMethodRequest.inputArguments = &inputArgument; callMethodRequest.methodId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_GETMONITOREDITEMS); callMethodRequest.objectId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER); UA_CallMethodResult result; UA_CallMethodResult_init(&result); result = UA_Server_call(server, &callMethodRequest); ck_assert_int_gt(result.inputArgumentResultsSize, 0); ck_assert_int_eq(result.inputArgumentResults[0], UA_STATUSCODE_BADTYPEMISMATCH); ck_assert_int_eq(result.statusCode, UA_STATUSCODE_BADINVALIDARGUMENT); UA_Array_delete(result.inputArgumentResults, result.inputArgumentResultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]); #endif } END_TEST int main(void) { Suite *s = suite_create("services_call"); TCase *tc_call = tcase_create("call - error branches"); tcase_add_checked_fixture(tc_call, setup, teardown); tcase_add_test(tc_call, callUnknownMethod); tcase_add_test(tc_call, callKnownMethodOnUnknownObject); tcase_add_test(tc_call, callMethodAndObjectExistsButMethodHasWrongNodeClass); tcase_add_test(tc_call, callMethodAndObjectExistsButNoFunctionPointerAttached); tcase_add_test(tc_call, callMethodNonExecutable); tcase_add_test(tc_call, callMethodOnUnrelatedObject); tcase_add_test(tc_call, callMethodWithMissingArguments); tcase_add_test(tc_call, callMethodWithTooManyArguments); tcase_add_test(tc_call, callMethodWithWronglyTypedArguments); suite_add_tcase(s, tc_call); 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); return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; }