diff --git a/.bazelrc b/.bazelrc index 0f4104e..ef11493 100644 --- a/.bazelrc +++ b/.bazelrc @@ -24,7 +24,7 @@ common --//score/datarouter/build_configuration_flags:persistent_logging=False common --//score/datarouter/build_configuration_flags:persistent_config_feature_enabled=False common --//score/datarouter/build_configuration_flags:enable_nonverbose_dlt=False common --//score/datarouter/build_configuration_flags:enable_dynamic_configuration_in_datarouter=False -common --//score/datarouter/build_configuration_flags:dlt_file_transfer_feature=False +common --//score/datarouter/build_configuration_flags:file_transfer=False common --//score/datarouter/build_configuration_flags:use_local_vlan=True build --incompatible_strict_action_env diff --git a/MODULE.bazel b/MODULE.bazel index cfd7162..a04b937 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -95,14 +95,14 @@ bazel_dep(name = "rapidjson", version = "1.1.0") bazel_dep(name = "score_communication", version = "0.1.2") git_override( module_name = "score_communication", - commit = "5a70133dd8bd632f5c07f200a5ee4bc9f507c23b", + commit = "42eb047495099ec70b5c8773f29783cc205bf7df", remote = "https://github.com/eclipse-score/communication.git", ) bazel_dep(name = "score_baselibs", version = "0.2.0") git_override( module_name = "score_baselibs", - commit = "3c65b223e9f516f95935bb4cd2e83d6088ca016f", + commit = "99d49637a2199f33a71edc479d39970e3bdcb271", remote = "https://github.com/eclipse-score/baselibs.git", ) diff --git a/score/datarouter/BUILD b/score/datarouter/BUILD index 77f4fdc..f685111 100644 --- a/score/datarouter/BUILD +++ b/score/datarouter/BUILD @@ -37,8 +37,8 @@ cc_library( strip_include_prefix = "include", visibility = [ "//score/datarouter/dlt_filetransfer_trigger_lib:__pkg__", - "//score/datarouter/file_transfer:__subpackages__", - "//score/datarouter/persistent_logging/persistent_logging:__pkg__", + "//score/datarouter/src/file_transfer:__subpackages__", + "//score/datarouter/src/persistent_logging/persistent_logging:__pkg__", "//score/datarouter/test:__subpackages__", ], deps = [ @@ -109,6 +109,7 @@ cc_library( "//score/datarouter/dynamic_configuration/i_session", "@score_baselibs//score/os:pthread", "@score_baselibs//score/os:sys_poll", + "@score_baselibs//score/quality/compiler_warnings", ], ) @@ -128,6 +129,7 @@ cc_library( "//score/datarouter/dynamic_configuration/i_session", "@score_baselibs//score/os:pthread", "@score_baselibs//score/os:sys_poll", + "@score_baselibs//score/quality/compiler_warnings", ], ) @@ -162,11 +164,11 @@ cc_library( features = COMPILER_WARNING_FEATURES, strip_include_prefix = "include", visibility = [ - "//score/datarouter/file_transfer:__subpackages__", "//score/datarouter/nonverbose_dlt:__pkg__", "//score/datarouter/nonverbose_dlt_stub:__pkg__", - "//score/datarouter/persistent_logging:__pkg__", - "//score/datarouter/persistent_logging/persistent_logging:__pkg__", + "//score/datarouter/src/file_transfer:__subpackages__", + "//score/datarouter/src/persistent_logging:__pkg__", + "//score/datarouter/src/persistent_logging/persistent_logging:__pkg__", "//score/datarouter/test:__subpackages__", ], deps = [ @@ -185,11 +187,11 @@ cc_library( features = COMPILER_WARNING_FEATURES, strip_include_prefix = "include", visibility = [ - "//score/datarouter/file_transfer:__subpackages__", "//score/datarouter/nonverbose_dlt:__pkg__", "//score/datarouter/nonverbose_dlt_stub:__pkg__", - "//score/datarouter/persistent_logging:__pkg__", - "//score/datarouter/persistent_logging/persistent_logging:__pkg__", + "//score/datarouter/src/file_transfer:__subpackages__", + "//score/datarouter/src/persistent_logging:__pkg__", + "//score/datarouter/src/persistent_logging/persistent_logging:__pkg__", ], deps = [ ":datarouter_types", @@ -448,7 +450,7 @@ cc_library( features = COMPILER_WARNING_FEATURES, strip_include_prefix = "include", visibility = [ - "//score/datarouter/file_transfer:__subpackages__", + "//score/datarouter/src/file_transfer:__subpackages__", ], deps = [ ":dltprotocol", @@ -492,7 +494,7 @@ cc_library( ], "//conditions:default": [], }) + select({ - "//score/datarouter/build_configuration_flags:config_dlt_file_transfer": ["DLT_FILE_TRANSFER_FEATURE"], + "//score/datarouter/build_configuration_flags:config_file_transfer": ["DLT_FILE_TRANSFER_FEATURE"], "//conditions:default": [], }) + select({ "//score/datarouter/build_configuration_flags:config_persistent_logging": ["PERSISTENT_LOGGING"], @@ -508,11 +510,11 @@ cc_library( ], "//conditions:default": ["//score/datarouter/src/persistency/stub_persistent_dictionary:stub_persistent_dictionary_factory"], }) + select({ - "//score/datarouter/build_configuration_flags:config_dlt_file_transfer": [ - "//score/datarouter/file_transfer/file_transfer_impl:file_transfer_stream_handler", + "//score/datarouter/build_configuration_flags:config_file_transfer": [ + "//score/datarouter/src/file_transfer/file_transfer_impl:file_transfer_stream_handler", ], "//conditions:default": [ - "//score/datarouter/file_transfer/file_transfer_stub:file_transfer_stream_handler_stub", + "//score/datarouter/src/file_transfer/file_transfer_stub:file_transfer_stream_handler_stub", ], }) + select({ "//score/datarouter/build_configuration_flags:config_datarouter_nonverbose_dlt": [ @@ -530,10 +532,10 @@ cc_library( ], }) + select({ "//score/datarouter/build_configuration_flags:config_persistent_logging": [ - "//score/datarouter/persistent_logging/persistent_logging_stub:sysedr_stub", + "//score/datarouter/src/persistent_logging/persistent_logging_stub:sysedr_stub", ], "//conditions:default": [ - "//score/datarouter/persistent_logging/persistent_logging_stub:sysedr_stub", + "//score/datarouter/src/persistent_logging/persistent_logging_stub:sysedr_stub", ], }), ) @@ -593,7 +595,7 @@ cc_library( ":udp_stream_output", ":unixdomain_server", "//score/datarouter/network:vlan", - "//score/datarouter/persistent_logging/persistent_logging_stub:sysedr_stub", + "//score/datarouter/src/persistent_logging/persistent_logging_stub:sysedr_stub", "@score_baselibs//score/language/futurecpp", "@score_baselibs//score/mw/log/configuration:nvconfig", "@score_baselibs//score/os:socket", @@ -760,7 +762,10 @@ cc_library( "//conditions:default": [], }), strip_include_prefix = "include", - visibility = ["//visibility:private"], + visibility = [ + "//score/datarouter:__pkg__", + "//score/datarouter/test:__subpackages__", + ], deps = [ ":datarouter_feature_config", ":datarouter_lib", @@ -770,7 +775,7 @@ cc_library( "@score_baselibs//score/mw/log/configuration:nvconfigfactory", ] + select({ "//score/datarouter/build_configuration_flags:config_persistent_logging": [ - "//score/datarouter/persistent_logging/persistent_logging_stub:sysedr_stub", + "//score/datarouter/src/persistent_logging/persistent_logging_stub:sysedr_stub", ], "//conditions:default": [], }), @@ -802,7 +807,7 @@ cc_library( "@score_baselibs//score/mw/log/configuration:nvconfigfactory", ] + select({ "//score/datarouter/build_configuration_flags:config_persistent_logging": [ - "//score/datarouter/persistent_logging/persistent_logging_stub:sysedr_stub", + "//score/datarouter/src/persistent_logging/persistent_logging_stub:sysedr_stub", ], "//conditions:default": [], }), @@ -858,8 +863,9 @@ cc_library( "//score/datarouter/daemon_communication:daemon_session_handle_interface", "//score/mw/log/detail/data_router:message_passing_interface", "//score/mw/log/detail/data_router/shared_memory:reader", + "@score_baselibs//score/concurrency", "@score_baselibs//score/os:pthread", - "@score_communication//score/mw/com/message_passing", + "@score_communication//score/message_passing", ], ) @@ -1037,7 +1043,7 @@ cc_library( ":log", "@score_baselibs//score/mw/log/configuration:nvconfig", "//score/datarouter/dlt_filetransfer_trigger_lib:filetransfer_message_types", - "//score/datarouter/persistent_logging/persistent_logging_stub:sysedr_stub", + "//score/datarouter/src/persistent_logging/persistent_logging_stub:sysedr_stub", ], ) @@ -1048,6 +1054,7 @@ cc_library( test_suite( name = "unit_tests", tests = [ + "//score/datarouter/lib/synchronized:synchronized_test", #"//score/datarouter/persistent_log_request:unit_tests", #"//score/datarouter/test:unit_tests", #"//score/datarouter/tools/generator:unit_tests", diff --git a/score/datarouter/build_configuration_flags/BUILD b/score/datarouter/build_configuration_flags/BUILD index e570218..08a775d 100644 --- a/score/datarouter/build_configuration_flags/BUILD +++ b/score/datarouter/build_configuration_flags/BUILD @@ -71,14 +71,14 @@ config_setting( ) bool_flag( - name = "dlt_file_transfer_feature", + name = "file_transfer", build_setting_default = True, ) config_setting( - name = "config_dlt_file_transfer", + name = "config_file_transfer", flag_values = { - ":dlt_file_transfer_feature": "True", + ":file_transfer": "True", }, visibility = [ "//score/datarouter:__subpackages__", diff --git a/score/datarouter/daemon_communication/BUILD b/score/datarouter/daemon_communication/BUILD index 136356e..e66e9ba 100644 --- a/score/datarouter/daemon_communication/BUILD +++ b/score/datarouter/daemon_communication/BUILD @@ -13,6 +13,18 @@ load("@rules_cc//cc:defs.bzl", "cc_library") load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") +load("@score_baselibs//score/quality/clang_tidy:extra_checks.bzl", "clang_tidy_extra_checks") + +clang_tidy_extra_checks( + name = "clang_tidy_extra_checks", + extra_features = [ + "spp_code_style_check_header_guards", + "spp_code_style_check_method_names", + "spp_code_style_check_readability", + "spp_code_style_check_type_names", + "spp_code_style_check_variable_names", + ], +) cc_library( name = "daemon_session_handle_interface", diff --git a/score/datarouter/daemon_communication/session_handle_interface.h b/score/datarouter/daemon_communication/session_handle_interface.h index 2d4a9df..b5a1691 100644 --- a/score/datarouter/daemon_communication/session_handle_interface.h +++ b/score/datarouter/daemon_communication/session_handle_interface.h @@ -26,7 +26,7 @@ namespace daemon class ISessionHandle { public: - virtual void AcquireRequest() const = 0; + virtual bool AcquireRequest() const = 0; virtual ~ISessionHandle() = default; }; diff --git a/score/datarouter/daemon_communication/session_handle_mock.h b/score/datarouter/daemon_communication/session_handle_mock.h index f09fe20..79df25c 100644 --- a/score/datarouter/daemon_communication/session_handle_mock.h +++ b/score/datarouter/daemon_communication/session_handle_mock.h @@ -24,7 +24,7 @@ namespace score::platform::internal::daemon::mock class SessionHandleMock : public score::platform::internal::daemon::ISessionHandle { public: - MOCK_METHOD(void, AcquireRequest, (), (const, override)); + MOCK_METHOD(bool, AcquireRequest, (), (const, override)); }; } // namespace score::platform::internal::daemon::mock diff --git a/score/datarouter/datarouter/data_router.cpp b/score/datarouter/datarouter/data_router.cpp index a64b307..b36b405 100644 --- a/score/datarouter/datarouter/data_router.cpp +++ b/score/datarouter/datarouter/data_router.cpp @@ -122,8 +122,6 @@ std::unique_ptr DataRouter::new_source_session_impl( std::unique_ptr reader, const score::mw::log::NvConfig& nvConfig) { - std::lock_guard lock(subscriber_mutex_); - auto sourceSession = std::make_unique(*this, std::move(reader), @@ -134,6 +132,19 @@ std::unique_ptr DataRouter::new_source_session_impl( quota_enforcement_enabled, stats_logger_, std::make_unique(nvConfig)); + + if (!sourceSession) + { + return nullptr; + } + + std::lock_guard lock(subscriber_mutex_); + + // Insert is protected by subscriber_mutex_ held by caller (new_source_session_impl). + // This relies on the calling convention that SourceSession is only constructed + // from new_source_session_impl() which acquires the lock before construction. + std::ignore = sources_.insert(sourceSession.get()); + if (sourceCallback_) { sourceCallback_(std::move(sourceSession->get_parser())); @@ -268,9 +279,8 @@ void DataRouter::SourceSession::processAndRouteLogMessages(uint64_t& message_cou if ((peek_bytes.has_value() && peek_bytes.value() > 0) || (cmd->ticks_without_write > kTicksWithoutAcquireWhileNoWrites)) { - cmd->acquire_requested = true; - request_acquire(); - needs_fast_reschedule = true; + cmd->acquire_requested = request_acquire(); + needs_fast_reschedule = cmd->acquire_requested; } else { @@ -279,9 +289,8 @@ void DataRouter::SourceSession::processAndRouteLogMessages(uint64_t& message_cou } else { - cmd->acquire_requested = true; - request_acquire(); - needs_fast_reschedule = true; + cmd->acquire_requested = request_acquire(); + needs_fast_reschedule = cmd->acquire_requested; } } } @@ -377,8 +386,6 @@ DataRouter::SourceSession::SourceSession(DataRouter& router, stats->name = name; } - std::ignore = router_.sources_.insert(this); - { auto stats = stats_data_.lock(); if (stats->name == "DR") @@ -524,19 +531,26 @@ void DataRouter::SourceSession::show_stats() } } -void DataRouter::SourceSession::request_acquire() +bool DataRouter::SourceSession::request_acquire() { - ++(stats_data_.lock()->count_acquire_requests); - - score::cpp::visit(score::cpp::overload( - [](UnixDomainServer::SessionHandle& handle) { - handle.pass_message("<"); - }, - [](score::cpp::pmr::unique_ptr& handle) { - handle->AcquireRequest(); - // For the quality team argumentation, kindly, check Ticket-200702 and Ticket-229594. - }), // LCOV_EXCL_LINE : tooling issue. no code to test in this line. - handle_); + const bool acquire_result = + score::cpp::visit(score::cpp::overload( + [](UnixDomainServer::SessionHandle& handle) { + handle.pass_message("<"); + return true; + }, + [](score::cpp::pmr::unique_ptr& handle) { + return handle->AcquireRequest(); + // For the quality team argumentation, kindly, check Ticket-200702 and Ticket-229594. + }), // LCOV_EXCL_LINE : tooling issue. no code to test in this line. + handle_); + + if (acquire_result) + { + ++(stats_data_.lock()->count_acquire_requests); + } + + return acquire_result; } void DataRouter::SourceSession::on_acquire_response(const score::mw::log::detail::ReadAcquireResult& acq) diff --git a/score/datarouter/datarouter/data_router.h b/score/datarouter/datarouter/data_router.h index 27d2926..0f86428 100644 --- a/score/datarouter/datarouter/data_router.h +++ b/score/datarouter/datarouter/data_router.h @@ -182,7 +182,7 @@ class DataRouter void on_closed_by_peer() override; void checkAndSetQuotaEnforcement(); - void request_acquire(); + bool request_acquire(); Synchronized local_subscriber_data_; Synchronized command_data_; diff --git a/score/datarouter/dlt_filetransfer_trigger_lib/BUILD b/score/datarouter/dlt_filetransfer_trigger_lib/BUILD index d910cbd..5e3eb57 100644 --- a/score/datarouter/dlt_filetransfer_trigger_lib/BUILD +++ b/score/datarouter/dlt_filetransfer_trigger_lib/BUILD @@ -13,6 +13,18 @@ load("@rules_cc//cc:defs.bzl", "cc_library") load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") +load("@score_baselibs//score/quality/clang_tidy:extra_checks.bzl", "clang_tidy_extra_checks") + +clang_tidy_extra_checks( + name = "clang_tidy_extra_checks", + extra_features = [ + "spp_code_style_check_header_guards", + "spp_code_style_check_method_names", + "spp_code_style_check_readability", + "spp_code_style_check_type_names", + "spp_code_style_check_variable_names", + ], +) ## =========================================================================== ## File transfer diff --git a/score/datarouter/dlt_filetransfer_trigger_lib/filetransfer_message.h b/score/datarouter/dlt_filetransfer_trigger_lib/filetransfer_message.h index 5fb864e..4f2a998 100644 --- a/score/datarouter/dlt_filetransfer_trigger_lib/filetransfer_message.h +++ b/score/datarouter/dlt_filetransfer_trigger_lib/filetransfer_message.h @@ -31,7 +31,7 @@ struct FileTransferEntry // Unavoidable use of of union type, in future dltit_t with LoggingIdentifier. // coverity[autosar_cpp14_a9_5_1_violation] see above score::platform::dltid_t ctxid{}; - std::string file_name = ""; + std::string file_name{}; uint8_t delete_file = 0; }; diff --git a/score/datarouter/dlt_filetransfer_trigger_lib/include/file_transfer.h b/score/datarouter/dlt_filetransfer_trigger_lib/include/file_transfer.h index 8a0c2da..5989082 100644 --- a/score/datarouter/dlt_filetransfer_trigger_lib/include/file_transfer.h +++ b/score/datarouter/dlt_filetransfer_trigger_lib/include/file_transfer.h @@ -11,8 +11,8 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -#ifndef BMW_LIB_LOGGING_FILE_TRANSFER_H -#define BMW_LIB_LOGGING_FILE_TRANSFER_H +#ifndef SCORE_PAS_LOGGING_DLT_FILETRANSFER_TRIGGER_LIB_INCLUDE_FILE_TRANSFER_H +#define SCORE_PAS_LOGGING_DLT_FILETRANSFER_TRIGGER_LIB_INCLUDE_FILE_TRANSFER_H #include "ifile_transfer.h" @@ -32,4 +32,4 @@ class FileTransfer final : public IFileTransfer } // namespace score::logging -#endif // BMW_LIB_LOGGING_IFILE_TRANSFER_H +#endif // SCORE_PAS_LOGGING_DLT_FILETRANSFER_TRIGGER_LIB_INCLUDE_FILE_TRANSFER_H diff --git a/score/datarouter/dlt_filetransfer_trigger_lib/include/ifile_transfer.h b/score/datarouter/dlt_filetransfer_trigger_lib/include/ifile_transfer.h index 12d8aad..373bc6c 100644 --- a/score/datarouter/dlt_filetransfer_trigger_lib/include/ifile_transfer.h +++ b/score/datarouter/dlt_filetransfer_trigger_lib/include/ifile_transfer.h @@ -11,8 +11,8 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -#ifndef AAS_PAS_LOGGING_DLT_FILETRANSFER_TRIGGER_LIB_INCLUDE_IFILE_TRANSFER_H -#define AAS_PAS_LOGGING_DLT_FILETRANSFER_TRIGGER_LIB_INCLUDE_IFILE_TRANSFER_H +#ifndef SCORE_PAS_LOGGING_DLT_FILETRANSFER_TRIGGER_LIB_INCLUDE_IFILE_TRANSFER_H +#define SCORE_PAS_LOGGING_DLT_FILETRANSFER_TRIGGER_LIB_INCLUDE_IFILE_TRANSFER_H #include #include @@ -44,4 +44,4 @@ using IFileTransferPtr = std::unique_ptr; } // namespace score::logging -#endif // AAS_PAS_LOGGING_DLT_FILETRANSFER_TRIGGER_LIB_INCLUDE_IFILE_TRANSFER_H +#endif // SCORE_PAS_LOGGING_DLT_FILETRANSFER_TRIGGER_LIB_INCLUDE_IFILE_TRANSFER_H diff --git a/score/datarouter/dynamic_configuration/config_session_factory/BUILD b/score/datarouter/dynamic_configuration/config_session_factory/BUILD index c94f857..b9a1343 100644 --- a/score/datarouter/dynamic_configuration/config_session_factory/BUILD +++ b/score/datarouter/dynamic_configuration/config_session_factory/BUILD @@ -13,6 +13,18 @@ load("@rules_cc//cc:defs.bzl", "cc_library") load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") +load("@score_baselibs//score/quality/clang_tidy:extra_checks.bzl", "clang_tidy_extra_checks") + +clang_tidy_extra_checks( + name = "clang_tidy_extra_checks", + extra_features = [ + "spp_code_style_check_header_guards", + "spp_code_style_check_method_names", + "spp_code_style_check_readability", + "spp_code_style_check_type_names", + "spp_code_style_check_variable_names", + ], +) [ cc_library( diff --git a/score/datarouter/dynamic_configuration/config_session_stub/BUILD b/score/datarouter/dynamic_configuration/config_session_stub/BUILD index fd8e2a5..1c1848c 100644 --- a/score/datarouter/dynamic_configuration/config_session_stub/BUILD +++ b/score/datarouter/dynamic_configuration/config_session_stub/BUILD @@ -13,6 +13,18 @@ load("@rules_cc//cc:defs.bzl", "cc_library") load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") +load("@score_baselibs//score/quality/clang_tidy:extra_checks.bzl", "clang_tidy_extra_checks") + +clang_tidy_extra_checks( + name = "clang_tidy_extra_checks", + extra_features = [ + "spp_code_style_check_header_guards", + "spp_code_style_check_method_names", + "spp_code_style_check_readability", + "spp_code_style_check_type_names", + "spp_code_style_check_variable_names", + ], +) [ cc_library( diff --git a/score/datarouter/dynamic_configuration/i_session/BUILD b/score/datarouter/dynamic_configuration/i_session/BUILD index 7cdb71f..c6450ed 100644 --- a/score/datarouter/dynamic_configuration/i_session/BUILD +++ b/score/datarouter/dynamic_configuration/i_session/BUILD @@ -13,6 +13,18 @@ load("@rules_cc//cc:defs.bzl", "cc_library") load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") +load("@score_baselibs//score/quality/clang_tidy:extra_checks.bzl", "clang_tidy_extra_checks") + +clang_tidy_extra_checks( + name = "clang_tidy_extra_checks", + extra_features = [ + "spp_code_style_check_header_guards", + "spp_code_style_check_method_names", + "spp_code_style_check_readability", + "spp_code_style_check_type_names", + "spp_code_style_check_variable_names", + ], +) cc_library( name = "i_session", diff --git a/score/datarouter/error/BUILD b/score/datarouter/error/BUILD index 237e02d..9fd0a03 100644 --- a/score/datarouter/error/BUILD +++ b/score/datarouter/error/BUILD @@ -13,6 +13,18 @@ load("@rules_cc//cc:defs.bzl", "cc_library") load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") +load("@score_baselibs//score/quality/clang_tidy:extra_checks.bzl", "clang_tidy_extra_checks") + +clang_tidy_extra_checks( + name = "clang_tidy_extra_checks", + extra_features = [ + "spp_code_style_check_header_guards", + "spp_code_style_check_method_names", + "spp_code_style_check_readability", + "spp_code_style_check_type_names", + "spp_code_style_check_variable_names", + ], +) cc_library( name = "logging_error", diff --git a/score/datarouter/error/error.cpp b/score/datarouter/error/error.cpp index 569dd2a..221b87f 100644 --- a/score/datarouter/error/error.cpp +++ b/score/datarouter/error/error.cpp @@ -50,12 +50,12 @@ std::string_view LoggingErrorDomain::MessageFor(const score::result::ErrorCode& namespace { -constexpr LoggingErrorDomain logging_error_domain; +constexpr LoggingErrorDomain kLoggingErrorDomain; } score::result::Error MakeError(const LoggingErrorCode code, const std::string_view user_message) noexcept { - return {static_cast(code), logging_error_domain, user_message}; + return {static_cast(code), kLoggingErrorDomain, user_message}; } } // namespace error diff --git a/score/datarouter/error/error.h b/score/datarouter/error/error.h index 5ab78b9..5685b74 100644 --- a/score/datarouter/error/error.h +++ b/score/datarouter/error/error.h @@ -11,8 +11,8 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -#ifndef DLT_LOGGING_ERROR_ERROR_H_ -#define DLT_LOGGING_ERROR_ERROR_H_ +#ifndef SCORE_PAS_LOGGING_ERROR_ERROR_H +#define SCORE_PAS_LOGGING_ERROR_ERROR_H #include "score/result/error.h" @@ -43,4 +43,4 @@ score::result::Error MakeError(const LoggingErrorCode code, const std::string_vi } // namespace logging } // namespace score -#endif // DLT_LOGGING_ERROR_ERROR_H_ +#endif // SCORE_PAS_LOGGING_ERROR_ERROR_H diff --git a/score/datarouter/include/applications/datarouter_feature_config.h b/score/datarouter/include/applications/datarouter_feature_config.h index 586b881..0799ce2 100644 --- a/score/datarouter/include/applications/datarouter_feature_config.h +++ b/score/datarouter/include/applications/datarouter_feature_config.h @@ -38,15 +38,15 @@ #endif #if defined(DLT_FILE_TRANSFER_FEATURE) -#include "score/datarouter/file_transfer/file_transfer_impl/filetransfer_stream.h" +#include "score/datarouter/src/file_transfer/file_transfer_impl/filetransfer_stream.h" #else -#include "score/datarouter/file_transfer/file_transfer_stub/file_transfer_stream_handler_stub.h" +#include "score/datarouter/src/file_transfer/file_transfer_stub/file_transfer_stream_handler_stub.h" #endif #if defined(PERSISTENT_LOGGING) -#include "score/datarouter/persistent_logging/persistent_logging/sysedr_concrete_factory.h" +#include "score/datarouter/src/persistent_logging/persistent_logging/sysedr_concrete_factory.h" #else -#include "score/datarouter/persistent_logging/persistent_logging_stub/stub_sysedr_factory.h" +#include "score/datarouter/src/persistent_logging/persistent_logging_stub/stub_sysedr_factory.h" #endif namespace score diff --git a/score/datarouter/include/daemon/dlt_log_server.h b/score/datarouter/include/daemon/dlt_log_server.h index 4da19b5..1879521 100644 --- a/score/datarouter/include/daemon/dlt_log_server.h +++ b/score/datarouter/include/daemon/dlt_log_server.h @@ -322,7 +322,7 @@ class DltLogServer : score::platform::datarouter::DltNonverboseHandlerType::IOut std::unique_ptr sysedr_handler_; - void sendNonVerbose(const score::mw::log::config::NvMsgDescriptor& desc, + void SendNonVerbose(const score::mw::log::config::NvMsgDescriptor& desc, uint32_t tmsp, const void* data, size_t size) override final; diff --git a/score/datarouter/include/daemon/message_passing_server.h b/score/datarouter/include/daemon/message_passing_server.h index 28ee774..3836534 100644 --- a/score/datarouter/include/daemon/message_passing_server.h +++ b/score/datarouter/include/daemon/message_passing_server.h @@ -18,15 +18,20 @@ #include "score/mw/log/detail/data_router/shared_memory/common.h" -#include "score/mw/com/message_passing/receiver_factory.h" -#include "score/mw/com/message_passing/sender_factory.h" +#include "score/message_passing/i_client_factory.h" +#include "score/message_passing/i_server_connection.h" +#include "score/message_passing/i_server_factory.h" #include "score/mw/log/detail/logging_identifier.h" #include "score/datarouter/daemon_communication/session_handle_interface.h" #include + +#include "score/concurrency/interruptible_wait.h" +#include #include #include #include +#include #include #include #include @@ -47,6 +52,19 @@ class IMessagePassingServerSessionWrapper virtual void EnqueueTickWhileLocked(pid_t /*pid*/) = 0; }; +/// QNX message passing server for handling logging client connections. +/// +/// Manages multiple client sessions and processes their log data asynchronously. +/// Uses a single worker thread to read from client shared memory buffers and route +/// log messages through the DataRouter pipeline. +/// +/// Threading model: +/// - Dispatch thread: Created by QnxDispatchEngine; receives connection requests and +/// messages via QNX message passing (dispatch_block loop in QnxDispatchEngine::RunOnThread) +/// - Worker thread: Processes session tick events to read shared memory and route logs +/// +/// Each client session is scheduled on the worker thread via a work queue to avoid +/// blocking the dispatch thread during potentially slow shared memory operations. class MessagePassingServer : public IMessagePassingServerSessionWrapper { public: @@ -55,17 +73,18 @@ class MessagePassingServer : public IMessagePassingServerSessionWrapper public: SessionHandle(pid_t pid, MessagePassingServer* server, - score::cpp::pmr::unique_ptr sender) - : daemon::ISessionHandle(), sender_(std::move(sender)), pid_(pid), server_(server) + score::cpp::pmr::unique_ptr sender) + : daemon::ISessionHandle(), sender_(std::move(sender)), pid_(pid), server_(server), sender_state_{} { } - void AcquireRequest() const override; + bool AcquireRequest() const override; private: - score::cpp::pmr::unique_ptr sender_; + score::cpp::pmr::unique_ptr sender_; pid_t pid_; MessagePassingServer* server_; + mutable std::optional sender_state_; }; class ISession @@ -83,19 +102,20 @@ class MessagePassingServer : public IMessagePassingServerSessionWrapper const score::mw::log::detail::ConnectMessageFromClient&, score::cpp::pmr::unique_ptr)>; - MessagePassingServer(SessionFactory factory, ::score::concurrency::Executor& executor); + MessagePassingServer(SessionFactory factory, + std::shared_ptr server_factory = nullptr, + std::shared_ptr client_factory = nullptr); ~MessagePassingServer() noexcept; // for unit test only. to keep rest of functions in private class MessagePassingServerForTest; private: - static score::mw::com::message_passing::ShortMessage PrepareAcquireRequestMessage() noexcept; - void NotifyAcquireRequestFailed(std::int32_t pid); - void OnConnectRequest(const score::mw::com::message_passing::MediumMessagePayload payload, const pid_t pid); - void OnAcquireResponse(const score::mw::com::message_passing::MediumMessagePayload payload, const pid_t pid); + void MessageCallback(const score::cpp::span message, const pid_t pid); + void OnConnectRequest(const score::cpp::span message, const pid_t pid); + void OnAcquireResponse(const score::cpp::span message, const pid_t pid); using timestamp_t = std::chrono::steady_clock::time_point; @@ -157,7 +177,7 @@ class MessagePassingServer : public IMessagePassingServerSessionWrapper SessionFactory factory_; - score::cpp::pmr::unique_ptr receiver_; + score::cpp::pmr::unique_ptr receiver_; std::mutex mutex_; score::cpp::stop_source stop_source_; @@ -166,9 +186,12 @@ class MessagePassingServer : public IMessagePassingServerSessionWrapper std::condition_variable worker_cond_; // to wake up worker thread std::unordered_map pid_session_map_; std::queue work_queue_; - bool workers_exit_; + std::atomic workers_exit_; std::condition_variable server_cond_; // to wake up server thread bool session_finishing_; + + std::shared_ptr server_factory_; + std::shared_ptr client_factory_; }; } // namespace internal diff --git a/score/datarouter/include/daemon/socketserver.h b/score/datarouter/include/daemon/socketserver.h index e6040e6..dce4823 100644 --- a/score/datarouter/include/daemon/socketserver.h +++ b/score/datarouter/include/daemon/socketserver.h @@ -27,6 +27,40 @@ #include #include #include +#include + +// Forward declaration for testing +namespace score +{ +namespace mw +{ +namespace log +{ +namespace detail +{ +class ConnectMessageFromClient; +} +} // namespace log +} // namespace mw +} // namespace score + +namespace score +{ +namespace os +{ +class Pthread; +} // namespace os +} // namespace score + +#ifdef __QNX__ +#include "score/message_passing/qnx_dispatch/qnx_dispatch_client_factory.h" +#include "score/message_passing/qnx_dispatch/qnx_dispatch_engine.h" +#include "score/message_passing/qnx_dispatch/qnx_dispatch_server_factory.h" +#else +#include "score/message_passing/unix_domain/unix_domain_client_factory.h" +#include "score/message_passing/unix_domain/unix_domain_engine.h" +#include "score/message_passing/unix_domain/unix_domain_server_factory.h" +#endif namespace score { @@ -35,6 +69,14 @@ namespace platform namespace datarouter { +#ifdef __QNX__ +using ServerFactory = score::message_passing::QnxDispatchServerFactory; +using ClientFactory = score::message_passing::QnxDispatchClientFactory; +#else +using ServerFactory = score::message_passing::UnixDomainServerFactory; +using ClientFactory = score::message_passing::UnixDomainClientFactory; +#endif + class SocketServer { public: @@ -50,6 +92,9 @@ class SocketServer static SocketServer server; server.doWork(exit_requested, no_adaptive_runtime); } + // static void run(const std::atomic_bool& exit_requested, const bool no_adaptive_runtime); + + /// @internal Test helpers - do not use in production code static PersistentStorageHandlers InitializePersistentStorage( std::unique_ptr& persistent_dictionary); @@ -93,6 +138,11 @@ class SocketServer DataRouter& router, score::logging::dltserver::DltLogServer& dlt_server, score::mw::log::Logger& stats_logger); + // Testable helper functions + static void SetThreadName() noexcept; + static void SetThreadName(score::os::Pthread& pthread_instance) noexcept; + static std::string ResolveSharedMemoryFileName(const score::mw::log::detail::ConnectMessageFromClient& conn, + const std::string& appid); private: void doWork(const std::atomic_bool& exit_requested, const bool no_adaptive_runtime); diff --git a/score/datarouter/include/unix_domain/unix_domain_common.h b/score/datarouter/include/unix_domain/unix_domain_common.h index 00bd37a..4f4861f 100644 --- a/score/datarouter/include/unix_domain/unix_domain_common.h +++ b/score/datarouter/include/unix_domain/unix_domain_common.h @@ -60,7 +60,7 @@ class UnixDomainSockAddr bool is_abstract() { - return !addr_.sun_path[0]; + return addr_.sun_path[0] == 0U; } struct sockaddr_un addr_; diff --git a/score/datarouter/lib/synchronized/BUILD b/score/datarouter/lib/synchronized/BUILD index a9b787f..cec9b01 100644 --- a/score/datarouter/lib/synchronized/BUILD +++ b/score/datarouter/lib/synchronized/BUILD @@ -12,6 +12,18 @@ # ******************************************************************************* load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") +load("@score_baselibs//score/quality/clang_tidy:extra_checks.bzl", "clang_tidy_extra_checks") + +clang_tidy_extra_checks( + name = "clang_tidy_extra_checks", + extra_features = [ + "spp_code_style_check_header_guards", + "spp_code_style_check_method_names", + "spp_code_style_check_readability", + "spp_code_style_check_type_names", + "spp_code_style_check_variable_names", + ], +) # Synchronized utility BUILD file diff --git a/score/datarouter/lib/synchronized/synchronized.h b/score/datarouter/lib/synchronized/synchronized.h index 404bfd4..789f649 100644 --- a/score/datarouter/lib/synchronized/synchronized.h +++ b/score/datarouter/lib/synchronized/synchronized.h @@ -11,8 +11,8 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -#ifndef SCORE_PAS_LOGGING_LIB_SYNCHRONIZED_SYNCHRONIZED_H_ -#define SCORE_PAS_LOGGING_LIB_SYNCHRONIZED_SYNCHRONIZED_H_ +#ifndef SCORE_PAS_LOGGING_LIB_SYNCHRONIZED_SYNCHRONIZED_H +#define SCORE_PAS_LOGGING_LIB_SYNCHRONIZED_SYNCHRONIZED_H #include #include @@ -48,7 +48,7 @@ class Synchronized public: template explicit Synchronized(Args&&... args) noexcept(std::is_nothrow_constructible_v) - : obj(std::forward(args)...) + : obj_(std::forward(args)...) { } @@ -60,37 +60,37 @@ class Synchronized [[nodiscard]] auto lock() { - auto unlocker = [ul = std::unique_lock(mut)](T*) mutable {}; - return std::unique_ptr(&obj, std::move(unlocker)); + auto unlocker = [ul = std::unique_lock(mut_)](T*) mutable {}; + return std::unique_ptr(&obj_, std::move(unlocker)); } [[nodiscard]] auto lock() const { - auto unlocker = [ul = std::unique_lock(mut)](const T*) mutable {}; - return std::unique_ptr(&obj, std::move(unlocker)); + auto unlocker = [ul = std::unique_lock(mut_)](const T*) mutable {}; + return std::unique_ptr(&obj_, std::move(unlocker)); } template - auto with_lock(Func&& f) + auto WithLock(Func&& f) { auto guard = lock(); return std::invoke(std::forward(f), *guard); } template - auto with_lock(Func&& f) const + auto WithLock(Func&& f) const { auto guard = lock(); return std::invoke(std::forward(f), *guard); } private: - mutable Mutex mut; - T obj; + mutable Mutex mut_; + T obj_; }; } // namespace datarouter } // namespace platform } // namespace score -#endif // SCORE_PAS_LOGGING_LIB_SYNCHRONIZED_SYNCHRONIZED_H_ +#endif // SCORE_PAS_LOGGING_LIB_SYNCHRONIZED_SYNCHRONIZED_H diff --git a/score/datarouter/lib/synchronized/synchronized_test.cpp b/score/datarouter/lib/synchronized/synchronized_test.cpp index f2d98cd..93fbc24 100644 --- a/score/datarouter/lib/synchronized/synchronized_test.cpp +++ b/score/datarouter/lib/synchronized/synchronized_test.cpp @@ -26,7 +26,7 @@ struct TestStruct { int value; - int sum() const + int Sum() const { return value + 50; } @@ -69,11 +69,11 @@ struct ParameterizedObject ParameterizedObject(int x_val, int y_val, const std::string& n) : x(x_val), y(y_val), name(n) {} - int sum() const + int Sum() const { return x + y; } - const std::string& get_name() const + const std::string& GetName() const { return name; } @@ -87,16 +87,16 @@ class SynchronizedUtilityTest : public ::testing::Test { Synchronized sync_value(42); - int result = sync_value.with_lock([](const auto& value) noexcept { + int result = sync_value.WithLock([](const auto& value) noexcept { return value; }); EXPECT_EQ(result, 42); - sync_value.with_lock([](auto& value) noexcept { + sync_value.WithLock([](auto& value) noexcept { value = 100; }); - result = sync_value.with_lock([](const auto& value) noexcept { + result = sync_value.WithLock([](const auto& value) noexcept { return value; }); EXPECT_EQ(result, 100); @@ -123,7 +123,7 @@ TEST_F(SynchronizedUtilityTest, TestThreadSafety) threads.push_back(std::thread([&counter]() noexcept { for (int j = 0; j < increments_per_thread; ++j) { - counter.with_lock([](auto& value) noexcept { + counter.WithLock([](auto& value) noexcept { ++value; }); } @@ -135,7 +135,7 @@ TEST_F(SynchronizedUtilityTest, TestThreadSafety) thread.join(); } - int result = counter.with_lock([](auto& value) noexcept { + int result = counter.WithLock([](auto& value) noexcept { return value; }); @@ -153,7 +153,7 @@ TEST_F(SynchronizedUtilityTest, LockMethodBasic) EXPECT_EQ(*locked_ptr, 100); } - sync_int.with_lock([](int& value) { + sync_int.WithLock([](int& value) { EXPECT_EQ(value, 100); }); } @@ -172,7 +172,7 @@ TEST_F(SynchronizedUtilityTest, TestStructLockReturnValue) EXPECT_EQ(locked_ptr->value, 100); EXPECT_EQ((*locked_ptr).value, 100); - EXPECT_EQ((*locked_ptr).sum(), 150); + EXPECT_EQ((*locked_ptr).Sum(), 150); } } @@ -185,10 +185,10 @@ TEST_F(SynchronizedUtilityTest, TestStructConstLockBehavior) EXPECT_EQ(const_locked_ptr->value, 42); // const_locked_ptr->value = 100; // Should not compile EXPECT_EQ((*const_locked_ptr).value, 42); - EXPECT_EQ((*const_locked_ptr).sum(), 92); + EXPECT_EQ((*const_locked_ptr).Sum(), 92); } - const_sync_struct.with_lock([](const TestStruct& s) noexcept { + const_sync_struct.WithLock([](const TestStruct& s) noexcept { EXPECT_EQ(s.value, 42); }); } @@ -197,20 +197,20 @@ TEST_F(SynchronizedUtilityTest, TestStructWithLockVariations) { Synchronized sync_struct(TestStruct{42}); - sync_struct.with_lock([](TestStruct& s) noexcept { + sync_struct.WithLock([](TestStruct& s) noexcept { s.value = 100; }); - int result = sync_struct.with_lock([](const TestStruct& s) noexcept { + int result = sync_struct.WithLock([](const TestStruct& s) noexcept { return s.value; }); EXPECT_EQ(result, 100); - sync_struct.with_lock([](TestStruct& s) noexcept { + sync_struct.WithLock([](TestStruct& s) noexcept { s.value = s.value * 2; }); - result = sync_struct.with_lock([](const TestStruct& s) noexcept { + result = sync_struct.WithLock([](const TestStruct& s) noexcept { return s.value; }); EXPECT_EQ(result, 200); @@ -220,7 +220,7 @@ TEST_F(SynchronizedUtilityTest, TestStructMemberFunctionPointers) { Synchronized sync_struct(TestStruct{42}); - auto result = sync_struct.with_lock([](const TestStruct& s) noexcept { + auto result = sync_struct.WithLock([](const TestStruct& s) noexcept { return s.value; }); EXPECT_EQ(result, 42); @@ -234,9 +234,9 @@ TEST_F(SynchronizedUtilityTest, TestStructFreeFunctionPointers) s.value = 999; }; - sync_struct.with_lock(process_struct); + sync_struct.WithLock(process_struct); - auto result = sync_struct.with_lock([](const TestStruct& s) noexcept { + auto result = sync_struct.WithLock([](const TestStruct& s) noexcept { return s.value; }); EXPECT_EQ(result, 999); @@ -252,7 +252,7 @@ TEST_F(SynchronizedUtilityTest, TestStructLockNonConst) EXPECT_EQ(locked_ptr->value, 77); } - auto result = sync_struct.with_lock([](const TestStruct& s) noexcept { + auto result = sync_struct.WithLock([](const TestStruct& s) noexcept { return s.value; }); EXPECT_EQ(result, 77); @@ -268,7 +268,7 @@ TEST_F(SynchronizedUtilityTest, TestStructLockConst) // const_sync_struct->value = 100; // Should not compile } - const_sync_struct.with_lock([](const TestStruct& s) noexcept { + const_sync_struct.WithLock([](const TestStruct& s) noexcept { EXPECT_EQ(s.value, 42); }); } @@ -280,22 +280,22 @@ TEST_F(SynchronizedUtilityTest, TestStructLambdaCaptureModes) int external_value = 10; // Test lambda with value capture - sync_struct.with_lock([external_value](TestStruct& s) noexcept { + sync_struct.WithLock([external_value](TestStruct& s) noexcept { s.value = external_value * 5; }); - auto result = sync_struct.with_lock([](const TestStruct& s) noexcept { + auto result = sync_struct.WithLock([](const TestStruct& s) noexcept { return s.value; }); EXPECT_EQ(result, 50); // Test lambda with reference capture int multiplier = 4; - sync_struct.with_lock([&multiplier](TestStruct& s) noexcept { + sync_struct.WithLock([&multiplier](TestStruct& s) noexcept { s.value = s.value * multiplier; }); - result = sync_struct.with_lock([](const TestStruct& s) noexcept { + result = sync_struct.WithLock([](const TestStruct& s) noexcept { return s.value; }); EXPECT_EQ(result, 200); @@ -305,11 +305,11 @@ TEST_F(SynchronizedUtilityTest, TestStructVoidReturningLambdas) { Synchronized sync_struct(TestStruct{42}); - sync_struct.with_lock([](TestStruct& s) noexcept -> void { + sync_struct.WithLock([](TestStruct& s) noexcept -> void { s.value = 123; }); - auto result = sync_struct.with_lock([](const TestStruct& s) noexcept { + auto result = sync_struct.WithLock([](const TestStruct& s) noexcept { return s.value; }); EXPECT_EQ(result, 123); @@ -319,7 +319,7 @@ TEST_F(SynchronizedUtilityTest, TestStructCopyVsMoveSemantics) { // Test construction from temporary (move semantics) Synchronized sync_struct1(TestStruct{42}); - auto val = sync_struct1.with_lock([](const TestStruct& s) noexcept { + auto val = sync_struct1.WithLock([](const TestStruct& s) noexcept { return s.value; }); EXPECT_EQ(val, 42); @@ -327,14 +327,14 @@ TEST_F(SynchronizedUtilityTest, TestStructCopyVsMoveSemantics) // Test construction from lvalue (copy semantics) TestStruct temp_struct{55}; Synchronized sync_struct2(temp_struct); - auto val1 = sync_struct2.with_lock([](const TestStruct& s) noexcept { + auto val1 = sync_struct2.WithLock([](const TestStruct& s) noexcept { return s.value; }); EXPECT_EQ(val1, 55); // Test in-place construction Synchronized sync_struct3(TestStruct{77}); - auto val2 = sync_struct3.with_lock([](const TestStruct& s) noexcept { + auto val2 = sync_struct3.WithLock([](const TestStruct& s) noexcept { return s.value; }); EXPECT_EQ(val2, 77); @@ -345,16 +345,16 @@ TEST_F(SynchronizedUtilityTest, TestMoveOnlyObject) { Synchronized sync_obj(150); - auto result = sync_obj.with_lock([](const auto& obj) noexcept { + auto result = sync_obj.WithLock([](const auto& obj) noexcept { return obj.value; }); EXPECT_EQ(result, 150); - sync_obj.with_lock([](auto& obj) noexcept { + sync_obj.WithLock([](auto& obj) noexcept { obj.value = 300; }); - auto val = sync_obj.with_lock([](const auto& obj) noexcept { + auto val = sync_obj.WithLock([](const auto& obj) noexcept { return obj.value; }); EXPECT_EQ(val, 300); @@ -365,16 +365,16 @@ TEST_F(SynchronizedUtilityTest, TestNoCopyNoMoveObject) { Synchronized sync_obj(250); - auto result = sync_obj.with_lock([](const auto& obj) noexcept { + auto result = sync_obj.WithLock([](const auto& obj) noexcept { return obj.value; }); EXPECT_EQ(result, 250); - sync_obj.with_lock([](auto& obj) noexcept { + sync_obj.WithLock([](auto& obj) noexcept { obj.value = 500; }); - auto val = sync_obj.with_lock([](const auto& obj) noexcept { + auto val = sync_obj.WithLock([](const auto& obj) noexcept { return obj.value; }); EXPECT_EQ(val, 500); @@ -385,16 +385,16 @@ TEST_F(SynchronizedUtilityTest, TestDefaultConstructedObject) { Synchronized sync_obj{}; - auto result = sync_obj.with_lock([](const auto& obj) noexcept { + auto result = sync_obj.WithLock([](const auto& obj) noexcept { return obj.value; }); EXPECT_EQ(result, 42); - sync_obj.with_lock([](auto& obj) noexcept { + sync_obj.WithLock([](auto& obj) noexcept { obj.value = 100; }); - auto val = sync_obj.with_lock([](const auto& obj) noexcept { + auto val = sync_obj.WithLock([](const auto& obj) noexcept { return obj.value; }); EXPECT_EQ(val, 100); @@ -405,23 +405,23 @@ TEST_F(SynchronizedUtilityTest, TestParameterizedConstruction) { Synchronized sync_obj(10, 20, "test_object"); - auto result = sync_obj.with_lock([](const auto& obj) noexcept { - return obj.sum(); + auto result = sync_obj.WithLock([](const auto& obj) noexcept { + return obj.Sum(); }); EXPECT_EQ(result, 30); - auto name = sync_obj.with_lock([](const auto& obj) noexcept { - return obj.get_name(); + auto name = sync_obj.WithLock([](const auto& obj) noexcept { + return obj.GetName(); }); EXPECT_EQ(name, "test_object"); - sync_obj.with_lock([](auto& obj) noexcept { + sync_obj.WithLock([](auto& obj) noexcept { obj.x = 15; obj.y = 25; }); - result = sync_obj.with_lock([](const auto& obj) noexcept { - return obj.sum(); + result = sync_obj.WithLock([](const auto& obj) noexcept { + return obj.Sum(); }); EXPECT_EQ(result, 40); } @@ -430,7 +430,7 @@ TEST_F(SynchronizedUtilityTest, TestConstOperations) { const Synchronized const_sync_struct(TestStruct{42}); - auto result = const_sync_struct.with_lock([](const TestStruct& s) noexcept { + auto result = const_sync_struct.WithLock([](const TestStruct& s) noexcept { return s.value; }); EXPECT_EQ(result, 42); @@ -451,26 +451,26 @@ TEST_F(SynchronizedUtilityTest, LockAndWithLock) EXPECT_EQ(locked_ptr->value, 150); } - sync_struct.with_lock([](TestStruct& s) noexcept { + sync_struct.WithLock([](TestStruct& s) noexcept { s.value = 200; }); - auto result = sync_struct.with_lock([](const TestStruct& s) { - return s.sum(); + auto result = sync_struct.WithLock([](const TestStruct& s) { + return s.Sum(); }); EXPECT_EQ(result, 250); - auto val1 = sync_struct.with_lock([](const TestStruct& s) noexcept { + auto val1 = sync_struct.WithLock([](const TestStruct& s) noexcept { return s.value; }); EXPECT_EQ(val1, 200); EXPECT_NO_THROW({ - sync_struct.with_lock([](const TestStruct& s) noexcept { + sync_struct.WithLock([](const TestStruct& s) noexcept { EXPECT_EQ(s.value, 200); }); }); - auto val2 = sync_struct.with_lock([](const TestStruct& s) noexcept { + auto val2 = sync_struct.WithLock([](const TestStruct& s) noexcept { return s.value; }); EXPECT_EQ(val2, 200); @@ -480,26 +480,26 @@ TEST_F(SynchronizedUtilityTest, TestStructExceptionSafetyDetailed) { Synchronized sync_struct(TestStruct{42}); - sync_struct.with_lock([](TestStruct& s) noexcept { + sync_struct.WithLock([](TestStruct& s) noexcept { s.value = 100; }); EXPECT_NO_THROW({ - sync_struct.with_lock([](TestStruct& s) noexcept { + sync_struct.WithLock([](TestStruct& s) noexcept { s.value = 999; EXPECT_EQ(s.value, 999); }); }); - auto val = sync_struct.with_lock([](const TestStruct& s) noexcept { + auto val = sync_struct.WithLock([](const TestStruct& s) noexcept { return s.value; }); EXPECT_EQ(val, 999); - sync_struct.with_lock([](TestStruct& s) noexcept { + sync_struct.WithLock([](TestStruct& s) noexcept { s.value = 200; }); - auto val2 = sync_struct.with_lock([](const TestStruct& s) noexcept { + auto val2 = sync_struct.WithLock([](const TestStruct& s) noexcept { return s.value; }); EXPECT_EQ(val2, 200); diff --git a/score/datarouter/network/BUILD b/score/datarouter/network/BUILD index 9aee0a2..6128587 100644 --- a/score/datarouter/network/BUILD +++ b/score/datarouter/network/BUILD @@ -12,6 +12,18 @@ # ******************************************************************************* load("@rules_cc//cc:defs.bzl", "cc_library") +load("@score_baselibs//score/quality/clang_tidy:extra_checks.bzl", "clang_tidy_extra_checks") + +clang_tidy_extra_checks( + name = "clang_tidy_extra_checks", + extra_features = [ + "spp_code_style_check_header_guards", + "spp_code_style_check_method_names", + "spp_code_style_check_readability", + "spp_code_style_check_type_names", + "spp_code_style_check_variable_names", + ], +) cc_library( name = "vlan", diff --git a/score/datarouter/network/vlan_local.h b/score/datarouter/network/vlan_local.h index d820d6c..a805db2 100644 --- a/score/datarouter/network/vlan_local.h +++ b/score/datarouter/network/vlan_local.h @@ -29,6 +29,10 @@ class Vlan : public ObjectSeam public: /// \brief thread-safe singleton accessor /// \return Either concrete OS-dependent instance or respective set mock instance + + // Follows naming convention of public Vlan API defined in score/network/vlan.h + // As this class serves as internal implementation stub + // NOLINTNEXTLINE(readability-identifier-naming): Justification above static Vlan& instance() noexcept; /// \brief Sets the IEEE 802.1Q PCP field for a given file descriptor to define the priority of the packets sent by diff --git a/score/datarouter/nonverbose_dlt_stub/BUILD b/score/datarouter/nonverbose_dlt_stub/BUILD index e4e672c..5ae03b5 100644 --- a/score/datarouter/nonverbose_dlt_stub/BUILD +++ b/score/datarouter/nonverbose_dlt_stub/BUILD @@ -13,6 +13,18 @@ load("@rules_cc//cc:defs.bzl", "cc_library") load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") +load("@score_baselibs//score/quality/clang_tidy:extra_checks.bzl", "clang_tidy_extra_checks") + +clang_tidy_extra_checks( + name = "clang_tidy_extra_checks", + extra_features = [ + "spp_code_style_check_header_guards", + "spp_code_style_check_method_names", + "spp_code_style_check_readability", + "spp_code_style_check_type_names", + "spp_code_style_check_variable_names", + ], +) [ cc_library( diff --git a/score/datarouter/nonverbose_dlt_stub/stub_nonverbose_dlt.h b/score/datarouter/nonverbose_dlt_stub/stub_nonverbose_dlt.h index 6bf0d2e..02937db 100644 --- a/score/datarouter/nonverbose_dlt_stub/stub_nonverbose_dlt.h +++ b/score/datarouter/nonverbose_dlt_stub/stub_nonverbose_dlt.h @@ -33,7 +33,7 @@ class StubDltNonverboseHandler : public LogParser::AnyHandler class IOutput { public: - virtual void sendNonVerbose(const score::mw::log::config::NvMsgDescriptor& desc, + virtual void SendNonVerbose(const score::mw::log::config::NvMsgDescriptor& desc, uint32_t tmsp, const void* data, size_t size) = 0; diff --git a/score/datarouter/src/applications/main_nonadaptive.cpp b/score/datarouter/src/applications/main_nonadaptive.cpp index 044e59e..7a2cbc1 100644 --- a/score/datarouter/src/applications/main_nonadaptive.cpp +++ b/score/datarouter/src/applications/main_nonadaptive.cpp @@ -16,6 +16,7 @@ // LCOV_EXCL_START #include +#include "score/os/errno_logging.h" #include "score/os/utils/signal_impl.h" #include "score/mw/log/logging.h" @@ -45,10 +46,23 @@ int main(std::int32_t argc, const char* argv[]) } score::os::SignalImpl sig{}; - // score::os wrappers are better suited for dependency injection, - // So suppressed here as it is safely used, and it is among safety headers. - // NOLINTNEXTLINE(score-banned-function) see comment above. - sig.signal(SIGTERM, signal_handler); + struct sigaction old_sigaction; + struct sigaction sig_handler; + sigset_t sig_set; + // NOLINTNEXTLINE(score-banned-function) This is testcode only, cf. line 3 + auto res = sig.SigEmptySet(sig_set); + if (!res.has_value()) + { + score::mw::log::LogError() << res.error(); + } + sig_handler.sa_handler = signal_handler; + sig_handler.sa_mask = sig_set; + sig_handler.sa_flags = 0; + res = sig.SigAction(SIGTERM, sig_handler, old_sigaction); + if (!res.has_value()) + { + score::mw::log::LogError() << res.error(); + } score::logging::datarouter::datarouter_app_init(); score::logging::datarouter::datarouter_app_run(exit_requested); score::logging::datarouter::datarouter_app_shutdown(); diff --git a/score/datarouter/src/daemon/dlt_log_server.cpp b/score/datarouter/src/daemon/dlt_log_server.cpp index 0d52c85..52543ba 100644 --- a/score/datarouter/src/daemon/dlt_log_server.cpp +++ b/score/datarouter/src/daemon/dlt_log_server.cpp @@ -30,7 +30,7 @@ namespace logging namespace dltserver { -void DltLogServer::sendNonVerbose(const score::mw::log::config::NvMsgDescriptor& desc, +void DltLogServer::SendNonVerbose(const score::mw::log::config::NvMsgDescriptor& desc, uint32_t tmsp, const void* data, size_t size) diff --git a/score/datarouter/src/daemon/message_passing_server.cpp b/score/datarouter/src/daemon/message_passing_server.cpp index ee89059..8e0e8df 100644 --- a/score/datarouter/src/daemon/message_passing_server.cpp +++ b/score/datarouter/src/daemon/message_passing_server.cpp @@ -14,6 +14,7 @@ #include "daemon/message_passing_server.h" #include "score/os/pthread.h" #include "score/os/unistd.h" +#include "score/mw/log/detail/data_router/message_passing_config.h" #include "score/memory.hpp" #include @@ -32,34 +33,7 @@ namespace internal { using score::mw::log::detail::DatarouterMessageIdentifier; -using score::mw::log::detail::ToMessageId; - -namespace -{ - -#if !defined(__QNXNTO__) - -// In Linux, MQueue size is usually limited to 10 for non-privileged processes -std::int32_t kReceiverQueueMaxSize = 10; - -#else - -std::int32_t kReceiverQueueMaxSize = 128; - -#endif - -constexpr std::uint32_t kConnectionTimeoutInMs = 1000UL; -constexpr std::int32_t kMaxNumbersOfRetry = 5; -constexpr std::chrono::milliseconds kSendRetryDelay{}; -constexpr std::chrono::milliseconds kConnectRetryDelay = std::chrono::milliseconds{5}; -/* - this functions is seen by this file only. -*/ -// LCOV_EXCL_START -void DropLogs(const score::mw::com::message_passing::LogFunction) {} -// LCOV_EXCL_STOP - -} // namespace +using score::mw::log::detail::MessagePassingConfig; void MessagePassingServer::SessionWrapper::enqueue_for_delete_while_locked(bool by_peer) { @@ -124,12 +98,21 @@ void MessagePassingServer::SessionWrapper::enqueue_tick_while_locked() */ // coverity[autosar_cpp14_a3_1_1_violation] MessagePassingServer::MessagePassingServer(MessagePassingServer::SessionFactory factory, - ::score::concurrency::Executor& executor) + std::shared_ptr server_factory, + std::shared_ptr client_factory) : IMessagePassingServerSessionWrapper(), factory_{std::move(factory)}, + mutex_{}, + stop_source_{}, connection_timeout_{}, + worker_cond_{}, + pid_session_map_{}, + work_queue_{}, workers_exit_{false}, - session_finishing_{false} + server_cond_{}, + session_finishing_{false}, + server_factory_{server_factory}, + client_factory_{client_factory} { worker_thread_ = score::cpp::jthread([this]() { RunWorkerThread(); @@ -141,24 +124,47 @@ MessagePassingServer::MessagePassingServer(MessagePassingServer::SessionFactory std::cerr << "setname_np: " << ret_pthread.error() << std::endl; } - using namespace ::score::mw::com::message_passing; - const std::string receiver_id{"/logging.datarouter_recv"}; - const std::vector allowed_uids{}; - score::mw::com::message_passing::ReceiverConfig receiver_config{}; - receiver_config.max_number_message_in_queue = kReceiverQueueMaxSize; - receiver_ = ReceiverFactory::Create(receiver_id, executor, allowed_uids, receiver_config); - - receiver_->Register(ToMessageId(DatarouterMessageIdentifier::kConnect), - [this](const MediumMessagePayload payload, const pid_t pid) noexcept { - this->OnConnectRequest(payload, pid); - }); - receiver_->Register(ToMessageId(DatarouterMessageIdentifier::kAcquireResponse), - [this](const MediumMessagePayload payload, const pid_t pid) noexcept { - this->OnAcquireResponse(payload, pid); - }); - - // all callbacks shall be registered before listening starts - auto ret_listening = receiver_->StartListening(); + const score::message_passing::ServiceProtocolConfig service_protocol_config{ + MessagePassingConfig::kDatarouterReceiverIdentifier, + MessagePassingConfig::kMaxMessageSize, + MessagePassingConfig::kMaxReplySize, + MessagePassingConfig::kMaxNotifySize}; + + const score::message_passing::IServerFactory::ServerConfig server_config{MessagePassingConfig::kMaxReceiverQueueSize, + MessagePassingConfig::kPreAllocConnections, + MessagePassingConfig::kMaxQueuedNotifies}; + receiver_ = server_factory_->Create(service_protocol_config, server_config); + + auto connect_callback = [](score::message_passing::IServerConnection& connection) noexcept -> std::uintptr_t { + const pid_t client_pid = connection.GetClientIdentity().pid; + return static_cast(client_pid); + }; + auto disconnect_callback = [this](score::message_passing::IServerConnection& connection) noexcept { + std::unique_lock lock(mutex_); + const auto found = pid_session_map_.find(connection.GetClientIdentity().pid); + if (found != pid_session_map_.end()) + { + SessionWrapper& wrapper = found->second; + wrapper.to_force_finish_ = true; + found->second.enqueue_for_delete_while_locked(true); + } + }; + auto received_send_message_callback = [this](score::message_passing::IServerConnection& connection, + const score::cpp::span message) noexcept -> score::cpp::blank { + const pid_t client_pid = connection.GetClientIdentity().pid; + this->MessageCallback(message, client_pid); + return {}; + }; + auto received_send_message_with_reply_callback = + [](score::message_passing::IServerConnection& /*connection*/, + score::cpp::span /*message*/) noexcept -> score::cpp::blank { + return {}; + }; + + auto ret_listening = receiver_->StartListening(connect_callback, + disconnect_callback, + received_send_message_callback, + received_send_message_with_reply_callback); if (!ret_listening) { std::cerr << "StartListening: " << ret_listening.error() << std::endl; @@ -190,10 +196,8 @@ MessagePassingServer::~MessagePassingServer() noexcept receiver_.reset(); // now, we can safely end the worker thread - { - std::lock_guard lock(mutex_); - workers_exit_ = true; - } + workers_exit_.store(true); + worker_cond_.notify_all(); if (worker_thread_.joinable()) { @@ -286,9 +290,15 @@ void MessagePassingServer::RunWorkerThread() lock.lock(); // LCOV_EXCL_STOP } - pid_session_map_.erase(pid); + // Extract the session wrapper to destroy it outside the mutex lock + // to avoid deadlock when destructor blocks (e.g., ClientConnection::~ClientConnection) + auto node = pid_session_map_.extract(pid); session_finishing_ = false; server_cond_.notify_all(); + lock.unlock(); + // Destroy the extracted node here (destructor runs without holding mutex) + node = {}; + lock.lock(); } else if (wrapper.reset_running_while_locked(requeue)) { @@ -298,7 +308,12 @@ void MessagePassingServer::RunWorkerThread() } else if (wrapper.is_marked_for_delete()) { - pid_session_map_.erase(pid); + // Extract the session wrapper to destroy it outside the mutex lock + auto node = pid_session_map_.extract(pid); + lock.unlock(); + // Destroy the extracted node here (destructor runs without holding mutex) + node = {}; + lock.lock(); } } } @@ -346,32 +361,58 @@ void MessagePassingServer::FinishPreviousSessionWhileLocked( }); } -void MessagePassingServer::OnConnectRequest(const score::mw::com::message_passing::MediumMessagePayload payload, - const pid_t pid) +void MessagePassingServer::MessageCallback(const score::cpp::span message, const pid_t pid) { - std::unique_lock lock(mutex_); - const auto found = pid_session_map_.find(pid); - if (found != pid_session_map_.end()) + if (message.size() < 1) { - // old pid owner died without us noticing, finish the old session - FinishPreviousSessionWhileLocked(found, lock); + std::cerr << "MessagePassingServer: Empty message received from " << pid; + return; + } + + const auto message_type = message.front(); + const auto payload = message.subspan(1); + switch (message_type) + { + case score::cpp::to_underlying(DatarouterMessageIdentifier::kConnect): + OnConnectRequest(payload, pid); + break; + case score::cpp::to_underlying(DatarouterMessageIdentifier::kAcquireResponse): + OnAcquireResponse(payload, pid); + break; + case score::cpp::to_underlying(DatarouterMessageIdentifier::kAcquireRequest): + std::cerr << "MessagePassingServer: Unsupported Acquire Message received from " << pid; + break; + default: + std::cerr << "MessagePassingServer: Unsupported MessageType received from " << pid; + break; } +} + +void MessagePassingServer::OnConnectRequest(const score::cpp::span message, const pid_t pid) +{ + score::mw::log::detail::ConnectMessageFromClient conn; - static_assert(payload.size() >= sizeof(conn), "ConnectMessageFromClient too big"); + if (message.size() < sizeof(conn)) + { + std::cout << "ConnectMessageFromClient too small" << std::endl; + stop_source_ = score::cpp::stop_source(); + return; + } /* Deviation from Rule M5-2-8: - Rule M5-2-8 (required, implementation, automated) An object with integer type or pointer to void type shall not be converted to an object with pointer type. Justification: - - This is safe since we convert void conn object to it's raw form to fill it from payload . + - This is safe since we convert void conn object to it's raw form to fill it from message . */ // coverity[autosar_cpp14_m5_2_8_violation] score::cpp::span conn_span{static_cast(static_cast(&conn)), sizeof(conn)}; - std::ignore = std::copy_n(payload.begin(), sizeof(conn), conn_span.begin()); + std::ignore = std::copy(message.begin(), message.end(), conn_span.begin()); auto appid_sv = conn.GetAppId().GetStringView(); std::string appid{appid_sv.data(), appid_sv.size()}; + // LCOV_EXCL_START: false positive since it is tested. std::string client_receiver_name; // LCOV_EXCL_STOP @@ -394,21 +435,14 @@ void MessagePassingServer::OnConnectRequest(const score::mw::com::message_passin client_receiver_name = std::string("/logging.") + appid + "." + std::to_string(conn.GetUid()); } - connection_timeout_ = timestamp_t::clock::now() + std::chrono::milliseconds(kConnectionTimeoutInMs); - auto stop_token = stop_source_.get_token(); - - lock.unlock(); - score::cpp::pmr::memory_resource* memory_resource = score::cpp::pmr::get_default_resource(); - constexpr score::mw::com::message_passing::SenderConfig sender_config{ - kMaxNumbersOfRetry, kSendRetryDelay, kConnectRetryDelay}; - auto sender = score::mw::com::message_passing::SenderFactory::Create( - client_receiver_name, stop_token, sender_config, &DropLogs, memory_resource); - - lock.lock(); + const score::message_passing::ServiceProtocolConfig& protocol_config{ + client_receiver_name, MessagePassingConfig::kMaxMessageSize, 0U, 0U}; + constexpr bool kTrulyAsyncSetToTrue = true; + const score::message_passing::IClientFactory::ClientConfig& client_config{0, 10, false, kTrulyAsyncSetToTrue, false}; - connection_timeout_ = timestamp_t{}; + auto sender = client_factory_->Create(protocol_config, client_config); // check for timeout or exit request if (stop_source_.stop_requested()) @@ -424,7 +458,6 @@ void MessagePassingServer::OnConnectRequest(const score::mw::com::message_passin // situation where one thread is blocked on the message passing server and // another thread is blocked on the subscriber mutex, is avoided by calling // the factory only with unlocked mutex. - lock.unlock(); ::score::cpp::pmr::unique_ptr session_handle{ ::score::cpp::pmr::make_unique(memory_resource, pid, this, std::move(sender))}; @@ -441,39 +474,39 @@ void MessagePassingServer::OnConnectRequest(const score::mw::com::message_passin std::cerr << "Fail to create session for pid: " << pid << std::endl; // LCOV_EXCL_STOP } - lock.lock(); if (session) { + std::unique_lock lock(mutex_); auto emplace_result = pid_session_map_.emplace(pid, SessionWrapper{this, pid, std::move(session)}); // enqueue the tick to speed up processing connection emplace_result.first->second.enqueue_tick_while_locked(); } } -void MessagePassingServer::OnAcquireResponse(const score::mw::com::message_passing::MediumMessagePayload payload, - const pid_t pid) +void MessagePassingServer::OnAcquireResponse(const score::cpp::span message, const pid_t pid) { std::lock_guard lock(mutex_); const auto found = pid_session_map_.find(pid); if (found != pid_session_map_.end()) { + auto& [key, session] = *found; + std::ignore = key; score::mw::log::detail::ReadAcquireResult acq{}; - static_assert(payload.size() >= sizeof(acq), "MwsrDataReadAcquired too big"); /* Deviation from Rule M5-2-8: - Rule M5-2-8 (required, implementation, automated) An object with integer type or pointer to void type shall not be converted to an object with pointer type. Justification: - - This is safe since we convert void acq_span object to it's raw form to fill it from payload . + - This is safe since we convert void acq_span object to it's raw form to fill it from message . */ // coverity[autosar_cpp14_m5_2_8_violation] score::cpp::span acq_span{static_cast(static_cast(&acq)), sizeof(acq)}; - std::ignore = std::copy_n(payload.begin(), sizeof(acq), acq_span.begin()); - found->second.session_->on_acquire_response(acq); + std::ignore = std::copy(message.begin(), message.end(), acq_span.begin()); + session.session_->on_acquire_response(acq); // enqueue the tick to speed up processing acquire response - found->second.enqueue_tick_while_locked(); + session.enqueue_tick_while_locked(); } } @@ -493,35 +526,31 @@ void MessagePassingServer::NotifyAcquireRequestFailed(std::int32_t pid) } found->second.enqueue_for_delete_while_locked(true); } -/* - Deviation from Rule A3-1-1: - - It shall be possible to include any header file - in multiple translation units without violating the One Definition Rule. - Justification: - - This is false positive. Function is declared only once. -*/ -// coverity[autosar_cpp14_a3_1_1_violation] -score::mw::com::message_passing::ShortMessage MessagePassingServer::PrepareAcquireRequestMessage() noexcept -{ - const auto my_pid = ::score::os::Unistd::instance().getpid(); - score::mw::com::message_passing::ShortMessage message{}; - message.id = ToMessageId(DatarouterMessageIdentifier::kAcquireRequest); - message.pid = my_pid; // the receiver will check if the pid matches the sending process - message.payload = 0; - return message; -} -void MessagePassingServer::SessionHandle::AcquireRequest() const +bool MessagePassingServer::SessionHandle::AcquireRequest() const { - const score::mw::com::message_passing::ShortMessage message = MessagePassingServer::PrepareAcquireRequestMessage(); + if (!sender_state_.has_value()) + { + sender_->Start(score::message_passing::IClientConnection::StateCallback{}, + score::message_passing::IClientConnection::NotifyCallback{}); + } + + sender_state_ = sender_->GetState(); + if (sender_state_ != score::message_passing::IClientConnection::State::kReady) + { + return false; + } + const std::array message{score::cpp::to_underlying(DatarouterMessageIdentifier::kAcquireRequest)}; auto ret = sender_->Send(message); if (!ret) { if (server_ != nullptr) { server_->NotifyAcquireRequestFailed(pid_); + return true; } } + return true; } } // namespace internal diff --git a/score/datarouter/src/daemon/socketserver.cpp b/score/datarouter/src/daemon/socketserver.cpp index 39671d5..2083e5d 100644 --- a/score/datarouter/src/daemon/socketserver.cpp +++ b/score/datarouter/src/daemon/socketserver.cpp @@ -55,13 +55,17 @@ constexpr auto* LOG_CHANNELS_PATH = "./etc/log-channels.json"; constexpr std::uint32_t statistics_log_period_us{10000000U}; constexpr std::uint32_t dlt_flush_period_us{100000U}; constexpr std::uint32_t throttle_time_us{100000U}; -/* - - this is local functions in this file so it cannot be tested -*/ -// LCOV_EXCL_START -void SetThreadName() noexcept + +} // namespace + +void SocketServer::SetThreadName() noexcept { - auto ret = score::os::Pthread::instance().setname_np(score::os::Pthread::instance().self(), "socketserver"); + SetThreadName(score::os::Pthread::instance()); +} + +void SocketServer::SetThreadName(score::os::Pthread& pthread_instance) noexcept +{ + auto ret = pthread_instance.setname_np(pthread_instance.self(), "socketserver"); if (!ret.has_value()) { auto errstr = ret.error().ToString(); @@ -69,15 +73,17 @@ void SetThreadName() noexcept } } -std::string ResolveSharedMemoryFileName(const score::mw::log::detail::ConnectMessageFromClient& conn, - const std::string& appid) +std::string SocketServer::ResolveSharedMemoryFileName(const score::mw::log::detail::ConnectMessageFromClient& conn, + const std::string& appid) { std::string return_file_name_string; // constuct the file from the 6 random chars if (true == conn.GetUseDynamicIdentifier()) { - std::string random_part; + // The LCOV considered the below line as uncovered which impossible according to the code flow, For the quality + // team argumentation, it may related to Ticket-213937 + std::string random_part{}; // LCOV_EXCL_LINE for (const auto& s : conn.GetRandomPart()) { random_part += s; @@ -91,9 +97,6 @@ std::string ResolveSharedMemoryFileName(const score::mw::log::detail::ConnectMes return_file_name_string += ".shmem"; return return_file_name_string; } -// LCOV_EXCL_STOP - -} // namespace SocketServer::PersistentStorageHandlers SocketServer::InitializePersistentStorage( std::unique_ptr& persistent_dictionary) @@ -103,11 +106,9 @@ SocketServer::PersistentStorageHandlers SocketServer::InitializePersistentStorag handlers.load_dlt = [&persistent_dictionary]() { return readDlt(*persistent_dictionary); }; - handlers.store_dlt = [&persistent_dictionary](const score::logging::dltserver::PersistentConfig& config) { writeDlt(config, *persistent_dictionary); }; - handlers.is_dlt_enabled = readDltEnabled(*persistent_dictionary); /* @@ -389,12 +390,10 @@ void SocketServer::doWork(const std::atomic_bool& exit_requested, const bool no_ std::placeholders::_2, // conn std::placeholders::_3); // handle - // Create message passing server with thread pool - // As documented in aas/mw/com/message_passing/design/README.md, the Receiver implementation will use just 1 thread - // from the thread pool for MQueue (Linux). For Resource Manager (QNX), it is supposed to use 2 threads. If it - // cannot allocate the second thread, it will work with just one thread, with reduced functionality (still enough - // for our use case, where every client's Sender runs on a dedicated thread) and likely with higher latency. - score::concurrency::ThreadPool executor{2}; + std::shared_ptr server_factory = std::make_shared(); + std::shared_ptr client_factory = + std::make_shared(/*server_factory_->GetEngine() Ticket-234313*/); + /* Deviation from Rule A5-1-4: - A lambda expression object shall not outlive any of its reference captured objects. @@ -402,7 +401,7 @@ void SocketServer::doWork(const std::atomic_bool& exit_requested, const bool no_ - mp_server does not exist inside any lambda. */ // coverity[autosar_cpp14_a5_1_4_violation: FALSE] - MessagePassingServer mp_server(mp_factory, executor); + MessagePassingServer mp_server(mp_factory, std::move(server_factory), std::move(client_factory)); // Run main event loop RunEventLoop(exit_requested, router, *dlt_server, stats_logger); diff --git a/score/datarouter/src/daemon/socketserver_config.cpp b/score/datarouter/src/daemon/socketserver_config.cpp index 1ce6e50..cdb26c1 100644 --- a/score/datarouter/src/daemon/socketserver_config.cpp +++ b/score/datarouter/src/daemon/socketserver_config.cpp @@ -196,7 +196,7 @@ score::logging::dltserver::PersistentConfig readDlt(IPersistentDictionary& pd) using rapidjson::ParseResult; score::logging::dltserver::PersistentConfig config{}; - const std::string json = pd.getString(CONFIG_DATABASE_KEY, "{}"); + const std::string json = pd.GetString(CONFIG_DATABASE_KEY, "{}"); Document d = createRJDocument(); ParseResult ok = d.Parse(json.c_str()); @@ -332,12 +332,12 @@ void writeDlt(const score::logging::dltserver::PersistentConfig& config, IPersis d.Accept(writer); const std::string json = buffer.GetString(); - pd.setString(CONFIG_DATABASE_KEY, json); + pd.SetString(CONFIG_DATABASE_KEY, json); } bool readDltEnabled(IPersistentDictionary& pd) { - const bool enabled = pd.getBool(CONFIG_OUTPUT_ENABLED_KEY, true); + const bool enabled = pd.GetBool(CONFIG_OUTPUT_ENABLED_KEY, true); if constexpr (kPersistentConfigFeatureEnabled) { std::cout << "Loaded output enable = " << enabled << " from KVS" << std::endl; @@ -347,7 +347,7 @@ bool readDltEnabled(IPersistentDictionary& pd) void writeDltEnabled(bool enabled, IPersistentDictionary& pd) { - pd.setBool(CONFIG_OUTPUT_ENABLED_KEY, enabled); + pd.SetBool(CONFIG_OUTPUT_ENABLED_KEY, enabled); } } // namespace datarouter diff --git a/score/datarouter/file_transfer/BUILD b/score/datarouter/src/file_transfer/BUILD similarity index 94% rename from score/datarouter/file_transfer/BUILD rename to score/datarouter/src/file_transfer/BUILD index 867f662..5de30db 100644 --- a/score/datarouter/file_transfer/BUILD +++ b/score/datarouter/src/file_transfer/BUILD @@ -21,7 +21,7 @@ cc_library( ], features = COMPILER_WARNING_FEATURES, visibility = [ - "//score/datarouter/file_transfer:__subpackages__", + "//score/datarouter/src/file_transfer:__subpackages__", "//score/datarouter/test:__subpackages__", ], deps = [ diff --git a/score/datarouter/file_transfer/file_transfer_handler_factory.hpp b/score/datarouter/src/file_transfer/file_transfer_handler_factory.hpp similarity index 100% rename from score/datarouter/file_transfer/file_transfer_handler_factory.hpp rename to score/datarouter/src/file_transfer/file_transfer_handler_factory.hpp diff --git a/score/datarouter/file_transfer/file_transfer_stub/BUILD b/score/datarouter/src/file_transfer/file_transfer_stub/BUILD similarity index 95% rename from score/datarouter/file_transfer/file_transfer_stub/BUILD rename to score/datarouter/src/file_transfer/file_transfer_stub/BUILD index 225dd3d..d550980 100644 --- a/score/datarouter/file_transfer/file_transfer_stub/BUILD +++ b/score/datarouter/src/file_transfer/file_transfer_stub/BUILD @@ -27,7 +27,7 @@ cc_library( deps = [ ":file_transfer_stream_handler_stub", "//score/datarouter:logparser", - "//score/datarouter/file_transfer:file_transfer_handler_factory", + "//score/datarouter/src/file_transfer:file_transfer_handler_factory", ], ) diff --git a/score/datarouter/file_transfer/file_transfer_stub/file_transfer_handler_factory_stub.h b/score/datarouter/src/file_transfer/file_transfer_stub/file_transfer_handler_factory_stub.h similarity index 91% rename from score/datarouter/file_transfer/file_transfer_stub/file_transfer_handler_factory_stub.h rename to score/datarouter/src/file_transfer/file_transfer_stub/file_transfer_handler_factory_stub.h index 420452f..15a36f1 100644 --- a/score/datarouter/file_transfer/file_transfer_stub/file_transfer_handler_factory_stub.h +++ b/score/datarouter/src/file_transfer/file_transfer_stub/file_transfer_handler_factory_stub.h @@ -15,8 +15,8 @@ #define PAS_LOGGING_FILE_TRANSFER_HANDLER_FACTORY_STUB_H #include "logparser/logparser.h" -#include "score/datarouter/file_transfer/file_transfer_handler_factory.hpp" -#include "score/datarouter/file_transfer/file_transfer_stub/file_transfer_stream_handler_stub.h" +#include "score/datarouter/src/file_transfer/file_transfer_handler_factory.hpp" +#include "score/datarouter/src/file_transfer/file_transfer_stub/file_transfer_stream_handler_stub.h" #include namespace score diff --git a/score/datarouter/file_transfer/file_transfer_stub/file_transfer_stream_handler_stub.h b/score/datarouter/src/file_transfer/file_transfer_stub/file_transfer_stream_handler_stub.h similarity index 100% rename from score/datarouter/file_transfer/file_transfer_stub/file_transfer_stream_handler_stub.h rename to score/datarouter/src/file_transfer/file_transfer_stub/file_transfer_stream_handler_stub.h diff --git a/score/datarouter/src/persistency/BUILD b/score/datarouter/src/persistency/BUILD index 9df2222..17ad2f0 100644 --- a/score/datarouter/src/persistency/BUILD +++ b/score/datarouter/src/persistency/BUILD @@ -13,6 +13,18 @@ load("@rules_cc//cc:defs.bzl", "cc_library") load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") +load("@score_baselibs//score/quality/clang_tidy:extra_checks.bzl", "clang_tidy_extra_checks") + +clang_tidy_extra_checks( + name = "clang_tidy_extra_checks", + extra_features = [ + "spp_code_style_check_header_guards", + "spp_code_style_check_method_names", + "spp_code_style_check_readability", + "spp_code_style_check_type_names", + "spp_code_style_check_variable_names", + ], +) cc_library( name = "interface", diff --git a/score/datarouter/src/persistency/i_persistent_dictionary.h b/score/datarouter/src/persistency/i_persistent_dictionary.h index a3d7ac1..2a83876 100644 --- a/score/datarouter/src/persistency/i_persistent_dictionary.h +++ b/score/datarouter/src/persistency/i_persistent_dictionary.h @@ -32,11 +32,11 @@ class IPersistentDictionary public: // Public interface shall be thread-safe. - virtual std::string getString(const std::string& key, const std::string& defaultValue) = 0; - virtual bool getBool(const std::string& key, const bool defaultValue) = 0; + virtual std::string GetString(const std::string& key, const std::string& default_value) = 0; + virtual bool GetBool(const std::string& key, const bool default_value) = 0; - virtual void setString(const std::string& key, const std::string& value) = 0; - virtual void setBool(const std::string& key, const bool value) = 0; + virtual void SetString(const std::string& key, const std::string& value) = 0; + virtual void SetBool(const std::string& key, const bool value) = 0; virtual ~IPersistentDictionary() = default; }; diff --git a/score/datarouter/src/persistency/mock_persistent_dictionary.h b/score/datarouter/src/persistency/mock_persistent_dictionary.h index 5616457..38f1313 100644 --- a/score/datarouter/src/persistency/mock_persistent_dictionary.h +++ b/score/datarouter/src/persistency/mock_persistent_dictionary.h @@ -29,11 +29,11 @@ namespace datarouter class MockPersistentDictionary : public ::score::platform::datarouter::IPersistentDictionary { public: - MOCK_METHOD(std::string, getString, (const std::string& key, const std::string& defaultValue), (override final)); - MOCK_METHOD(bool, getBool, (const std::string& key, const bool defaultValue), (override final)); + MOCK_METHOD(std::string, GetString, (const std::string& key, const std::string& defaultValue), (override final)); + MOCK_METHOD(bool, GetBool, (const std::string& key, const bool defaultValue), (override final)); - MOCK_METHOD(void, setString, (const std::string& key, const std::string& value), (override final)); - MOCK_METHOD(void, setBool, (const std::string& key, const bool value), (override final)); + MOCK_METHOD(void, SetString, (const std::string& key, const std::string& value), (override final)); + MOCK_METHOD(void, SetBool, (const std::string& key, const bool value), (override final)); protected: ~MockPersistentDictionary() = default; diff --git a/score/datarouter/src/persistency/persistent_dictionary_factory.hpp b/score/datarouter/src/persistency/persistent_dictionary_factory.hpp index d097a5c..4b31576 100644 --- a/score/datarouter/src/persistency/persistent_dictionary_factory.hpp +++ b/score/datarouter/src/persistency/persistent_dictionary_factory.hpp @@ -11,8 +11,8 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -#ifndef SCORE_PAS_LOGGING_SRC_PERSISTENT_DICTIONARY_FACTORY_HPP -#define SCORE_PAS_LOGGING_SRC_PERSISTENT_DICTIONARY_FACTORY_HPP +#ifndef SCORE_PAS_LOGGING_SRC_PERSISTENCY_PERSISTENT_DICTIONARY_FACTORY_HPP +#define SCORE_PAS_LOGGING_SRC_PERSISTENCY_PERSISTENT_DICTIONARY_FACTORY_HPP #include "i_persistent_dictionary.h" #include @@ -60,4 +60,4 @@ class PersistentDictionaryFactory } // namespace platform } // namespace score -#endif // SCORE_PAS_LOGGING_SRC_PERSISTENT_DICTIONARY_FACTORY_HPP +#endif // SCORE_PAS_LOGGING_SRC_PERSISTENCY_PERSISTENT_DICTIONARY_FACTORY_HPP diff --git a/score/datarouter/src/persistency/stub_persistent_dictionary/stub_persistent_dictionary.cpp b/score/datarouter/src/persistency/stub_persistent_dictionary/stub_persistent_dictionary.cpp index ebae94b..42cdc25 100644 --- a/score/datarouter/src/persistency/stub_persistent_dictionary/stub_persistent_dictionary.cpp +++ b/score/datarouter/src/persistency/stub_persistent_dictionary/stub_persistent_dictionary.cpp @@ -20,21 +20,21 @@ namespace platform namespace datarouter { -std::string StubPersistentDictionary::getString(const std::string& /*key*/, const std::string& defaultValue) +std::string StubPersistentDictionary::GetString(const std::string& /*key*/, const std::string& default_value) { - return defaultValue; + return default_value; } -bool StubPersistentDictionary::getBool(const std::string& /*key*/, const bool defaultValue) +bool StubPersistentDictionary::GetBool(const std::string& /*key*/, const bool default_value) { - return defaultValue; + return default_value; } // LCOV_EXCL_START : will suppress this function since there nothing to assert through UT as its void empty function -void StubPersistentDictionary::setString(const std::string& /*key*/, const std::string& /*value*/) {} +void StubPersistentDictionary::SetString(const std::string& /*key*/, const std::string& /*value*/) {} // LCOV_EXCL_STOP // LCOV_EXCL_START : will suppress this function since there nothing to assert through UT as its void empty function -void StubPersistentDictionary::setBool(const std::string& /*key*/, const bool /*value*/) {} +void StubPersistentDictionary::SetBool(const std::string& /*key*/, const bool /*value*/) {} // LCOV_EXCL_STOP } // namespace datarouter diff --git a/score/datarouter/src/persistency/stub_persistent_dictionary/stub_persistent_dictionary.h b/score/datarouter/src/persistency/stub_persistent_dictionary/stub_persistent_dictionary.h index be38514..2e2ceb6 100644 --- a/score/datarouter/src/persistency/stub_persistent_dictionary/stub_persistent_dictionary.h +++ b/score/datarouter/src/persistency/stub_persistent_dictionary/stub_persistent_dictionary.h @@ -26,11 +26,11 @@ namespace datarouter class StubPersistentDictionary final : public IPersistentDictionary { public: - std::string getString(const std::string& key, const std::string& defaultValue) override; - bool getBool(const std::string& key, const bool defaultValue) override; + std::string GetString(const std::string& key, const std::string& default_value) override; + bool GetBool(const std::string& key, const bool default_value) override; - void setString(const std::string& key, const std::string& value) override; - void setBool(const std::string& key, const bool value) override; + void SetString(const std::string& key, const std::string& value) override; + void SetBool(const std::string& key, const bool value) override; }; } // namespace datarouter diff --git a/score/datarouter/persistent_logging/BUILD b/score/datarouter/src/persistent_logging/BUILD similarity index 64% rename from score/datarouter/persistent_logging/BUILD rename to score/datarouter/src/persistent_logging/BUILD index 194f120..7811b88 100644 --- a/score/datarouter/persistent_logging/BUILD +++ b/score/datarouter/src/persistent_logging/BUILD @@ -12,13 +12,25 @@ # ******************************************************************************* load("@rules_cc//cc:defs.bzl", "cc_library") +load("@score_baselibs//score/quality/clang_tidy:extra_checks.bzl", "clang_tidy_extra_checks") + +clang_tidy_extra_checks( + name = "clang_tidy_extra_checks", + extra_features = [ + "spp_code_style_check_header_guards", + "spp_code_style_check_method_names", + "spp_code_style_check_readability", + "spp_code_style_check_type_names", + "spp_code_style_check_variable_names", + ], +) cc_library( name = "sysedr_handler_interface", hdrs = [ "isysedr_handler.h", ], - visibility = ["//score/datarouter/persistent_logging:__subpackages__"], + visibility = ["//score/datarouter/src/persistent_logging:__subpackages__"], deps = [ "//score/datarouter:logparser", ], @@ -30,7 +42,7 @@ cc_library( "sysedr_factory.hpp", ], visibility = [ - "//score/datarouter/persistent_logging:__subpackages__", + "//score/datarouter/src/persistent_logging:__subpackages__", "//score/datarouter/test:__subpackages__", ], deps = [ diff --git a/score/datarouter/persistent_logging/isysedr_handler.h b/score/datarouter/src/persistent_logging/isysedr_handler.h similarity index 86% rename from score/datarouter/persistent_logging/isysedr_handler.h rename to score/datarouter/src/persistent_logging/isysedr_handler.h index 922d2d9..f4a0c9f 100644 --- a/score/datarouter/persistent_logging/isysedr_handler.h +++ b/score/datarouter/src/persistent_logging/isysedr_handler.h @@ -11,8 +11,8 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -#ifndef SCORE_PAS_LOGGING_INCLUDE_SYSEDR_ISYSEDR_HANDLER_H -#define SCORE_PAS_LOGGING_INCLUDE_SYSEDR_ISYSEDR_HANDLER_H +#ifndef SCORE_PAS_LOGGING_SRC_PERSISTENT_LOGGING_ISYSEDR_HANDLER_H +#define SCORE_PAS_LOGGING_SRC_PERSISTENT_LOGGING_ISYSEDR_HANDLER_H #include "logparser/logparser.h" @@ -47,4 +47,4 @@ class ISysedrHandler : public LogParser::TypeHandler, public LogParser::AnyHandl } // namespace platform } // namespace score -#endif // SCORE_PAS_LOGGING_INCLUDE_SYSEDR_ISYSEDR_HANDLER_H +#endif // SCORE_PAS_LOGGING_SRC_PERSISTENT_LOGGING_ISYSEDR_HANDLER_H diff --git a/score/datarouter/persistent_logging/persistent_logging_stub/BUILD b/score/datarouter/src/persistent_logging/persistent_logging_stub/BUILD similarity index 86% rename from score/datarouter/persistent_logging/persistent_logging_stub/BUILD rename to score/datarouter/src/persistent_logging/persistent_logging_stub/BUILD index 3f9fb32..a66bf6e 100644 --- a/score/datarouter/persistent_logging/persistent_logging_stub/BUILD +++ b/score/datarouter/src/persistent_logging/persistent_logging_stub/BUILD @@ -26,7 +26,7 @@ cc_library( visibility = ["//score/datarouter:__subpackages__"], deps = [ "//score/datarouter:log", - "//score/datarouter/persistent_logging:sysedr_factory", - "//score/datarouter/persistent_logging:sysedr_handler_interface", + "//score/datarouter/src/persistent_logging:sysedr_factory", + "//score/datarouter/src/persistent_logging:sysedr_handler_interface", ], ) diff --git a/score/datarouter/persistent_logging/persistent_logging_stub/stub_sysedr_factory.cpp b/score/datarouter/src/persistent_logging/persistent_logging_stub/stub_sysedr_factory.cpp similarity index 91% rename from score/datarouter/persistent_logging/persistent_logging_stub/stub_sysedr_factory.cpp rename to score/datarouter/src/persistent_logging/persistent_logging_stub/stub_sysedr_factory.cpp index d14c004..6803f8c 100644 --- a/score/datarouter/persistent_logging/persistent_logging_stub/stub_sysedr_factory.cpp +++ b/score/datarouter/src/persistent_logging/persistent_logging_stub/stub_sysedr_factory.cpp @@ -11,7 +11,7 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -#include "score/datarouter/persistent_logging/persistent_logging_stub/stub_sysedr_factory.h" +#include "score/datarouter/src/persistent_logging/persistent_logging_stub/stub_sysedr_factory.h" #include "score/mw/log/logging.h" namespace score diff --git a/score/datarouter/persistent_logging/persistent_logging_stub/stub_sysedr_factory.h b/score/datarouter/src/persistent_logging/persistent_logging_stub/stub_sysedr_factory.h similarity index 65% rename from score/datarouter/persistent_logging/persistent_logging_stub/stub_sysedr_factory.h rename to score/datarouter/src/persistent_logging/persistent_logging_stub/stub_sysedr_factory.h index 54594c8..fc4866f 100644 --- a/score/datarouter/persistent_logging/persistent_logging_stub/stub_sysedr_factory.h +++ b/score/datarouter/src/persistent_logging/persistent_logging_stub/stub_sysedr_factory.h @@ -11,11 +11,11 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -#ifndef SCORE_PAS_LOGGING_INCLUDE_SYSEDR_STUB_SYSEDR_FACTORY_H -#define SCORE_PAS_LOGGING_INCLUDE_SYSEDR_STUB_SYSEDR_FACTORY_H +#ifndef SCORE_PAS_LOGGING_SRC_PERSISTENT_LOGGING_PERSISTENT_LOGGING_STUB_STUB_SYSEDR_FACTORY_H +#define SCORE_PAS_LOGGING_SRC_PERSISTENT_LOGGING_PERSISTENT_LOGGING_STUB_STUB_SYSEDR_FACTORY_H -#include "score/datarouter/persistent_logging/persistent_logging_stub/stub_sysedr_handler.h" -#include "score/datarouter/persistent_logging/sysedr_factory.hpp" +#include "score/datarouter/src/persistent_logging/persistent_logging_stub/stub_sysedr_handler.h" +#include "score/datarouter/src/persistent_logging/sysedr_factory.hpp" namespace score { @@ -34,4 +34,4 @@ class StubSysedrFactory : public SysedrFactory } // namespace platform } // namespace score -#endif // SCORE_PAS_LOGGING_INCLUDE_SYSEDR_STUB_SYSEDR_FACTORY_H +#endif // SCORE_PAS_LOGGING_SRC_PERSISTENT_LOGGING_PERSISTENT_LOGGING_STUB_STUB_SYSEDR_FACTORY_H diff --git a/score/datarouter/persistent_logging/persistent_logging_stub/stub_sysedr_handler.cpp b/score/datarouter/src/persistent_logging/persistent_logging_stub/stub_sysedr_handler.cpp similarity index 81% rename from score/datarouter/persistent_logging/persistent_logging_stub/stub_sysedr_handler.cpp rename to score/datarouter/src/persistent_logging/persistent_logging_stub/stub_sysedr_handler.cpp index 694540f..8d48028 100644 --- a/score/datarouter/persistent_logging/persistent_logging_stub/stub_sysedr_handler.cpp +++ b/score/datarouter/src/persistent_logging/persistent_logging_stub/stub_sysedr_handler.cpp @@ -11,7 +11,7 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -#include "score/datarouter/persistent_logging/persistent_logging_stub/stub_sysedr_handler.h" +#include "score/datarouter/src/persistent_logging/persistent_logging_stub/stub_sysedr_handler.h" namespace score { @@ -25,7 +25,7 @@ namespace internal void StubSysedrHandler::handle(timestamp_t /* timestamp */, const char* data, bufsize_t size) {} // LogParser::AnyHandler -void StubSysedrHandler::handle(const TypeInfo& typeInfo, timestamp_t timestamp, const char* data, bufsize_t size) {} +void StubSysedrHandler::handle(const TypeInfo& type_info, timestamp_t timestamp, const char* data, bufsize_t size) {} // LCOV_EXCL_STOP diff --git a/score/datarouter/persistent_logging/persistent_logging_stub/stub_sysedr_handler.h b/score/datarouter/src/persistent_logging/persistent_logging_stub/stub_sysedr_handler.h similarity index 66% rename from score/datarouter/persistent_logging/persistent_logging_stub/stub_sysedr_handler.h rename to score/datarouter/src/persistent_logging/persistent_logging_stub/stub_sysedr_handler.h index 9c34524..e23be0e 100644 --- a/score/datarouter/persistent_logging/persistent_logging_stub/stub_sysedr_handler.h +++ b/score/datarouter/src/persistent_logging/persistent_logging_stub/stub_sysedr_handler.h @@ -11,10 +11,10 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -#ifndef SCORE_PAS_LOGGING_INCLUDE_SYSEDR_STUB_SYSEDR_HANDLER_H -#define SCORE_PAS_LOGGING_INCLUDE_SYSEDR_STUB_SYSEDR_HANDLER_H +#ifndef SCORE_PAS_LOGGING_SRC_PERSISTENT_LOGGING_PERSISTENT_LOGGING_STUB_STUB_SYSEDR_HANDLER_H +#define SCORE_PAS_LOGGING_SRC_PERSISTENT_LOGGING_PERSISTENT_LOGGING_STUB_STUB_SYSEDR_HANDLER_H -#include "score/datarouter/persistent_logging/isysedr_handler.h" +#include "score/datarouter/src/persistent_logging/isysedr_handler.h" namespace score { @@ -31,11 +31,11 @@ class StubSysedrHandler final : public ISysedrHandler void handle(timestamp_t /* timestamp */, const char* data, bufsize_t size) override; // LogParser::AnyHandler - void handle(const TypeInfo& typeInfo, timestamp_t timestamp, const char* data, bufsize_t size) override; + void handle(const TypeInfo& type_info, timestamp_t timestamp, const char* data, bufsize_t size) override; }; } // namespace internal } // namespace platform } // namespace score -#endif // SCORE_PAS_LOGGING_INCLUDE_SYSEDR_STUB_SYSEDR_HANDLER_H +#endif // SCORE_PAS_LOGGING_SRC_PERSISTENT_LOGGING_PERSISTENT_LOGGING_STUB_STUB_SYSEDR_HANDLER_H diff --git a/score/datarouter/persistent_logging/sysedr_factory.hpp b/score/datarouter/src/persistent_logging/sysedr_factory.hpp similarity index 84% rename from score/datarouter/persistent_logging/sysedr_factory.hpp rename to score/datarouter/src/persistent_logging/sysedr_factory.hpp index 186dbd1..fad6fee 100644 --- a/score/datarouter/persistent_logging/sysedr_factory.hpp +++ b/score/datarouter/src/persistent_logging/sysedr_factory.hpp @@ -11,10 +11,10 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -#ifndef SCORE_PAS_LOGGING_INCLUDE_SYSEDR_SYSEDR_FACTORY_H -#define SCORE_PAS_LOGGING_INCLUDE_SYSEDR_SYSEDR_FACTORY_H +#ifndef SCORE_PAS_LOGGING_SRC_PERSISTENT_LOGGING_SYSEDR_FACTORY_HPP +#define SCORE_PAS_LOGGING_SRC_PERSISTENT_LOGGING_SYSEDR_FACTORY_HPP -#include "score/datarouter/persistent_logging/isysedr_handler.h" +#include "score/datarouter/src/persistent_logging/isysedr_handler.h" #include namespace score @@ -54,4 +54,4 @@ class SysedrFactory } // namespace platform } // namespace score -#endif // SCORE_PAS_LOGGING_INCLUDE_SYSEDR_SYSEDR_FACTORY_H +#endif // SCORE_PAS_LOGGING_SRC_PERSISTENT_LOGGING_SYSEDR_FACTORY_HPP diff --git a/score/datarouter/src/unix_domain/unix_domain_server.cpp b/score/datarouter/src/unix_domain/unix_domain_server.cpp index 49bf6cc..a3c3d77 100644 --- a/score/datarouter/src/unix_domain/unix_domain_server.cpp +++ b/score/datarouter/src/unix_domain/unix_domain_server.cpp @@ -18,6 +18,7 @@ #include "score/os/sys_poll.h" #include "score/os/unistd.h" #include "score/os/utils/signal_impl.h" +#include "score/quality/compiler_warnings/warnings.h" #include #include @@ -230,22 +231,11 @@ void UnixDomainServer::process_server_iteration(ConnectionState& state, // NOLINTBEGIN(score-banned-function) see comment above score::cpp::expected poll_ret; #ifdef __QNX__ -// NOLINTBEGIN(score-banned-preprocessor-directives) : required due to compiler warning for qnx -/* -Deviation from Rule A16-7-1: -- The #pragma directive shall not be used -Justification: -- required due to compiler warning for qnx -*/ -// coverity[autosar_cpp14_a16_7_1_violation] see above -#pragma GCC diagnostic push -// coverity[autosar_cpp14_a16_7_1_violation] see above -#pragma GCC diagnostic ignored "-Wuseless-cast" + DISABLE_WARNING_PUSH + DISABLE_WARNING_USELESS_CAST poll_ret = score::os::SysPoll::instance().poll(state.connection_pollfd_list.data(), static_cast(size), timeout); -// coverity[autosar_cpp14_a16_7_1_violation] see above -#pragma GCC diagnostic pop -// NOLINTEND(score-banned-preprocessor-directives) + DISABLE_WARNING_POP #else poll_ret = score::os::SysPoll::instance().poll(state.connection_pollfd_list.data(), size, timeout); #endif diff --git a/score/datarouter/test/ut/ut_logging/BUILD b/score/datarouter/test/ut/ut_logging/BUILD index 3979213..0f92458 100644 --- a/score/datarouter/test/ut/ut_logging/BUILD +++ b/score/datarouter/test/ut/ut_logging/BUILD @@ -133,7 +133,7 @@ cc_test( "filetransferTest.cpp", ], defines = select({ - "//score/datarouter/build_configuration_flags:config_dlt_file_transfer": ["DLT_FILE_TRANSFER_FEATURE"], + "//score/datarouter/build_configuration_flags:config_file_transfer": ["DLT_FILE_TRANSFER_FEATURE"], "//conditions:default": [], }), features = FEAT_COMPILER_WARNINGS_AS_ERRORS, @@ -152,14 +152,14 @@ cc_test( tags = ["unit"], deps = [ "//score/datarouter:datarouter_feature_config", - "//score/datarouter/file_transfer:file_transfer_handler_factory", + "//score/datarouter/src/file_transfer:file_transfer_handler_factory", "@googletest//:gtest_main", ] + select({ - "//score/datarouter/build_configuration_flags:config_dlt_file_transfer": [ - "//score/datarouter/file_transfer/file_transfer_impl:file_transfer_stream_handler_factory", + "//score/datarouter/build_configuration_flags:config_file_transfer": [ + "//score/datarouter/src/file_transfer/file_transfer_impl:file_transfer_stream_handler_factory", ], "//conditions:default": [ - "//score/datarouter/file_transfer/file_transfer_stub:file_transfer_handler_factory_stub", + "//score/datarouter/src/file_transfer/file_transfer_stub:file_transfer_handler_factory_stub", ], }), ) @@ -216,7 +216,7 @@ cc_test( ], "//conditions:default": [], }) + select({ - "//score/datarouter/build_configuration_flags:config_dlt_file_transfer": ["test_filetransfer_stream.cpp"], + "//score/datarouter/build_configuration_flags:config_file_transfer": ["test_filetransfer_stream.cpp"], "//conditions:default": [], }), features = FEAT_COMPILER_WARNINGS_AS_ERRORS, @@ -313,6 +313,7 @@ cc_test( "//score/datarouter/src/persistency:mock", "@googletest//:gtest_main", "@score_baselibs//score/mw/log/configuration:nvconfig_mock", + "@score_baselibs//score/os/mocklib:pthread_mock", "@score_baselibs//score/os/mocklib:unistd_mock", ], ) @@ -330,7 +331,7 @@ cc_test( "@googletest//:gtest_main", "@score_baselibs//score/os/mocklib:pthread_mock", "@score_baselibs//score/os/mocklib:unistd_mock", - "@score_communication//score/mw/com/message_passing:mock", + "@score_communication//score/message_passing:mock", ], ) diff --git a/score/datarouter/test/ut/ut_logging/test_dltserver.cpp b/score/datarouter/test/ut/ut_logging/test_dltserver.cpp index 9f3303a..c81c89d 100644 --- a/score/datarouter/test/ut/ut_logging/test_dltserver.cpp +++ b/score/datarouter/test/ut/ut_logging/test_dltserver.cpp @@ -28,24 +28,6 @@ namespace platform inline namespace { -bool operator<(const dltid_t c1, const dltid_t c2) -{ - return c1.value < c2.value; -} - -bool operator==(const std::vector& c1, const std::vector& c2) -{ - if (c1.size() != c2.size()) - { - return false; - } - std::vector c1s{c1}; - std::sort(c1s.begin(), c1s.end()); - std::vector c2s{c2}; - std::sort(c2s.begin(), c2s.end()); - return std::equal(c1s.begin(), c1s.end(), c2s.begin()); -} - } // namespace } // namespace platform @@ -60,7 +42,6 @@ namespace dltserver inline namespace { -constexpr auto kSizeChannelName{4UL}; // Declared those constants for readability purposes constexpr auto kCommandSize{1UL}; constexpr auto kCommandResponseSize{1UL}; @@ -95,51 +76,6 @@ constexpr auto kCommandResponseSize{1UL}; } \ } while (0) -#define EXPECT_NAMES_OR_NOOP(resp, n1, n2) \ - do \ - { \ - if (!(resp).empty()) \ - { \ - EXPECT_GE((resp).size(), kCommandResponseSize); \ - EXPECT_THAT((resp).substr(1, (resp).size()), ::testing::HasSubstr(n1)); \ - EXPECT_THAT((resp).substr(1, (resp).size()), ::testing::HasSubstr(n2)); \ - } \ - else \ - { \ - SUCCEED(); \ - } \ - } while (0) - -bool operator==(const PersistentConfig::ChannelDescription& c1, const PersistentConfig::ChannelDescription& c2) -{ - return c1.channelThreshold == c2.channelThreshold; -} - -bool operator==(const PersistentConfig& c1, const PersistentConfig& c2) -{ - if (c1.channels != c2.channels) - { - return false; - } - if (c1.filteringEnabled != c2.filteringEnabled) - { - return false; - } - if (c1.defaultThreshold != c2.defaultThreshold) - { - return false; - } - if (c1.channelAssignments != c2.channelAssignments) - { - return false; - } - if (c1.messageThresholds != c2.messageThresholds) - { - return false; - } - return true; -} - } // namespace } // namespace dltserver @@ -172,7 +108,7 @@ class DltLogServer::DltLogServerTest : public DltLogServer public: using DltLogServer::sendFTVerbose; - using DltLogServer::sendNonVerbose; + using DltLogServer::SendNonVerbose; using DltLogServer::sendVerbose; }; @@ -270,18 +206,6 @@ class DltServerCreatedWithConfigFixture : public ::testing::Test false}; PersistentConfig pConfig{}; - PersistentConfig pConfigCompare{ - { - {"DFLT", {score::mw::log::LogLevel::kFatal}}, - {"CORE", {score::mw::log::LogLevel::kError}}, - }, - true, - score::mw::log::LogLevel::kOff, - { - {dltid_t("APP0"), {{dltid_t("CTX0"), bothChannels}}}, - }, - {{dltid_t("APP0"), {{dltid_t("CTX0"), score::mw::log::LogLevel::kOff}}}}, - }; testing::StrictMock> readCallback_; testing::StrictMock> writeCallback_; @@ -289,22 +213,6 @@ class DltServerCreatedWithConfigFixture : public ::testing::Test LogSenderMock* log_sender_mock_raw_ptr_{nullptr}; }; -TEST_F(DltServerCreatedWithConfigFixture, WhenCreatedWithConfig) -{ - EXPECT_CALL(readCallback_, Call()).Times(1).WillOnce(Return(pConfig)); - EXPECT_CALL(writeCallback_, Call(pConfigCompare)).Times(::testing::AtMost(1)); - - DltLogServer dltServer(sConfig, readCallback_.AsStdFunction(), writeCallback_.AsStdFunction(), true); - - std::string response{}; - auto session = dltServer.new_config_session( - score::platform::datarouter::ConfigSessionHandleType{0, nullptr, std::reference_wrapper{response}}); - - session->on_command(std::string(kCommandSize, config::STORE_DLT_CONFIG)); - - EXPECT_OK_OR_NOOP(response); -} - TEST_F(DltServerCreatedWithConfigFixture, FlushChannelsExpectNoThrowException) { EXPECT_CALL(readCallback_, Call()).Times(1).WillOnce(Return(pConfig)); @@ -334,82 +242,6 @@ TEST_F(DltServerCreatedWithConfigFixture, GetQuotaCorrectWrongAppNameExpectDefau EXPECT_EQ(ret_val, 1.0); } -TEST_F(DltServerCreatedWithConfigFixture, ReadLogChannelsWithShortNameCommandExpectChannelsList) -{ - using namespace std::string_literals; - - EXPECT_CALL(readCallback_, Call()).Times(1).WillOnce(Return(pConfig)); - EXPECT_CALL(writeCallback_, Call(_)).Times(0); - - auto config_short_channel_name = sConfig; - config_short_channel_name.channels = { - // channels as std::unordered_map - {dltid_t("DFLT"), {dltid_t("ECU0"), "", 3490U, "", 3491U, score::mw::log::LogLevel::kFatal, "160.48.199.34"}}, - {dltid_t("SR"), {dltid_t("ECU0"), "", 3489U, "", 3493U, score::mw::log::LogLevel::kError, "160.48.199.101"}}, - {dltid_t("CORE"), {dltid_t("ECU0"), "", 3490U, "", 3492U, score::mw::log::LogLevel::kError, "160.48.199.101"}}, - }; - // Extend SetUp expectation of the channel construction: - EXPECT_CALL(outputs_, construct(_, _, 3493U, Eq(std::string("160.48.199.101")))).Times(1); - EXPECT_CALL(outputs_, bind(_, _, 3489U)).Times(1); - - DltLogServer dltServer( - config_short_channel_name, readCallback_.AsStdFunction(), writeCallback_.AsStdFunction(), true); - - std::string response{}; - auto session = dltServer.new_config_session( - score::platform::datarouter::ConfigSessionHandleType{0, nullptr, std::reference_wrapper{response}}); - - session->on_command(std::string(kCommandSize, config::READ_LOG_CHANNEL_NAMES)); - - const std::string kResponse = {"CORESR\0\0DFLT"s}; - // Verify content only when dynamic configuration is enabled - if (!response.empty()) - { - EXPECT_EQ(response.size(), kCommandResponseSize + 3 * kSizeChannelName); - EXPECT_THAT(response.substr(1, response.size()), ::testing::HasSubstr("DFLT")); - EXPECT_THAT(response.substr(1, response.size()), ::testing::HasSubstr("SR\0\0"s)); - EXPECT_THAT(response.substr(1, response.size()), ::testing::HasSubstr("CORE")); - } - else - { - SUCCEED(); - } -} - -TEST_F(DltServerCreatedWithConfigFixture, ReadLogChannelsCommandExpectChannelsList) -{ - EXPECT_CALL(readCallback_, Call()).Times(1).WillOnce(Return(pConfig)); - EXPECT_CALL(writeCallback_, Call(_)).Times(0); - - DltLogServer dltServer(sConfig, readCallback_.AsStdFunction(), writeCallback_.AsStdFunction(), true); - - std::string response{}; - std::reference_wrapper reference_to_response{response}; - auto session = dltServer.new_config_session( - score::platform::datarouter::ConfigSessionHandleType{0, nullptr, reference_to_response}); - - session->on_command(std::string(kCommandSize, config::READ_LOG_CHANNEL_NAMES)); - - const std::string kResponse{"COREDFLT"}; - EXPECT_NAMES_OR_NOOP(response, "DFLT", "CORE"); -} - -TEST_F(DltServerCreatedWithConfigFixture, ResetToDefaultCommandExpectTwoReadCallbacks) -{ - EXPECT_CALL(readCallback_, Call()).Times(::testing::AtMost(2)).WillOnce(Return(pConfig)); - EXPECT_CALL(writeCallback_, Call(_)).Times(::testing::AtMost(1)); - - DltLogServer dltServer(sConfig, readCallback_.AsStdFunction(), writeCallback_.AsStdFunction(), true); - - std::string response{}; - auto session = dltServer.new_config_session( - score::platform::datarouter::ConfigSessionHandleType{0, nullptr, std::reference_wrapper{response}}); - - session->on_command(std::string(kCommandSize, config::RESET_TO_DEFAULT)); - - EXPECT_OK_OR_NOOP(response); -} - TEST(ResetToDefaultTest, ResetToDefaultCommandEmptyChannelsNoReadCallback) { testing::StrictMock outputs; @@ -555,41 +387,6 @@ TEST_F(DltServerCreatedWithConfigFixture, SetDefaultTraceStateCommandExpectReadC EXPECT_OK_OR_NOOP(response); } -TEST_F(DltServerCreatedWithConfigFixture, SetLogChannelAssignmentWrongCommandExpectReadCallback) -{ - EXPECT_CALL(readCallback_, Call()).Times(1).WillOnce(Return(pConfig)); - EXPECT_CALL(writeCallback_, Call(_)).Times(0); - - DltLogServer dltServer(sConfig, readCallback_.AsStdFunction(), writeCallback_.AsStdFunction(), true); - - std::string response{}; - auto session = dltServer.new_config_session( - score::platform::datarouter::ConfigSessionHandleType{0, nullptr, std::reference_wrapper{response}}); - - session->on_command(std::string(kCommandSize, config::SET_LOG_CHANNEL_ASSIGNMENT)); - - EXPECT_ERR_OR_NOOP(response); -} - -TEST_F(DltServerCreatedWithConfigFixture, SetLogChannelAssignmentCommandNoChannelsExpectReadCallback) -{ - EXPECT_CALL(readCallback_, Call()).Times(1).WillOnce(Return(pConfig)); - EXPECT_CALL(writeCallback_, Call(_)).Times(0); - - DltLogServer dltServer(sConfig, readCallback_.AsStdFunction(), writeCallback_.AsStdFunction(), true); - - std::string response{}; - auto session = dltServer.new_config_session( - score::platform::datarouter::ConfigSessionHandleType{0, nullptr, std::reference_wrapper{response}}); - - std::array command_buffer{ - config::SET_LOG_CHANNEL_ASSIGNMENT, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}; - const std::string command{command_buffer.begin(), command_buffer.end()}; - session->on_command(command); - - EXPECT_ERR_OR_NOOP(response); -} - TEST_F(DltServerCreatedWithConfigFixture, SetLogChannelAssignmentCommandFoundChannelAssignmentFoundExpectReadCallback) { EXPECT_CALL(readCallback_, Call()).Times(1).WillOnce(Return(pConfig)); @@ -711,86 +508,38 @@ TEST_F(DltServerCreatedWithConfigFixture, SetLogChannelAssignmentWrongChannel) EXPECT_EQ(response[0], config::RET_ERROR); } -TEST_F(DltServerCreatedWithConfigFixture, SetDltOutputEnableWrongCommandExpectReadCallback) +TEST_F(DltServerCreatedWithConfigFixture, SetLogChannelAssignmentBehaviorRemovesChannel) { EXPECT_CALL(readCallback_, Call()).Times(1).WillOnce(Return(pConfig)); EXPECT_CALL(writeCallback_, Call(_)).Times(0); - DltLogServer dltServer(sConfig, readCallback_.AsStdFunction(), writeCallback_.AsStdFunction(), true); - - std::string response{}; - auto session = dltServer.new_config_session( - score::platform::datarouter::ConfigSessionHandleType{0, nullptr, std::reference_wrapper{response}}); - - session->on_command(std::string(kCommandSize, config::SET_DLT_OUTPUT_ENABLE)); - - EXPECT_ERR_OR_NOOP(response); -} - -TEST_F(DltServerCreatedWithConfigFixture, SetDltOutputEnableCommandEnableExpectReadCallback) -{ - EXPECT_CALL(readCallback_, Call()).Times(1).WillOnce(Return(pConfig)); - EXPECT_CALL(writeCallback_, Call(_)).Times(0); - - DltLogServer dltServer(sConfig, readCallback_.AsStdFunction(), writeCallback_.AsStdFunction(), true); - - std::string response{}; - auto session = dltServer.new_config_session( - score::platform::datarouter::ConfigSessionHandleType{0, nullptr, std::reference_wrapper{response}}); - - std::array command_buffer{config::SET_DLT_OUTPUT_ENABLE, 1}; - const std::string command{command_buffer.begin(), command_buffer.end()}; - session->on_command(command); - - const auto dlt_enabled = dltServer.GetDltEnabled(); - if (!response.empty()) - { - EXPECT_TRUE(dlt_enabled); - } - - EXPECT_OK_OR_NOOP(response); -} - -TEST_F(DltServerCreatedWithConfigFixture, SetDltOutputEnableCommandDisableExpectReadCallback) -{ - EXPECT_CALL(readCallback_, Call()).Times(1).WillOnce(Return(pConfig)); - EXPECT_CALL(writeCallback_, Call(_)).Times(0); - - DltLogServer dltServer(sConfig, readCallback_.AsStdFunction(), writeCallback_.AsStdFunction(), true); - - std::string response{}; - auto session = dltServer.new_config_session( - score::platform::datarouter::ConfigSessionHandleType{0, nullptr, std::reference_wrapper{response}}); - - std::array command_buffer{config::SET_DLT_OUTPUT_ENABLE, 0}; - const std::string command{command_buffer.begin(), command_buffer.end()}; - session->on_command(command); - - const auto dlt_enabled = dltServer.GetDltEnabled(); - if (!response.empty()) - { - EXPECT_FALSE(dlt_enabled); - } - - EXPECT_OK_OR_NOOP(response); -} + // Setup: add CORE so APP0/CTX0 is routed to DFLT + CORE. + score::logging::dltserver::DltLogServer::DltLogServerTest dltServer( + sConfig, readCallback_.AsStdFunction(), writeCallback_.AsStdFunction(), true, std::move(log_sender_mock_)); -TEST_F(DltServerCreatedWithConfigFixture, SetDltOutputEnableCommandWrongValueExpectReadCallback) -{ - EXPECT_CALL(readCallback_, Call()).Times(1).WillOnce(Return(pConfig)); - EXPECT_CALL(writeCallback_, Call(_)).Times(0); + const score::mw::log::detail::LoggingIdentifier app_id{"APP0"}; + const score::mw::log::detail::LoggingIdentifier ctx_id{"CTX0"}; + const score::mw::log::detail::log_entry_deserialization::LogEntryDeserializationReflection entry{ + app_id, ctx_id, {}, 0, score::mw::log::LogLevel::kOff}; - DltLogServer dltServer(sConfig, readCallback_.AsStdFunction(), writeCallback_.AsStdFunction(), true); + const auto resp_add = + dltServer.SetLogChannelAssignment(dltid_t{"APP0"}, dltid_t{"CTX0"}, dltid_t{"CORE"}, AssignmentAction::Add); + ASSERT_FALSE(resp_add.empty()); + EXPECT_EQ(resp_add[0], static_cast(config::RET_OK)); - std::string response{}; - auto session = dltServer.new_config_session( - score::platform::datarouter::ConfigSessionHandleType{0, nullptr, std::reference_wrapper{response}}); + // With both channels assigned: 2 sends. + EXPECT_CALL(*log_sender_mock_raw_ptr_, SendVerbose(_, _, _)).Times(2); + dltServer.sendVerbose(100U, entry); + ::testing::Mock::VerifyAndClearExpectations(log_sender_mock_raw_ptr_); - std::array command_buffer{config::SET_DLT_OUTPUT_ENABLE, 2}; - const std::string command{command_buffer.begin(), command_buffer.end()}; - session->on_command(command); + const auto resp_remove = + dltServer.SetLogChannelAssignment(dltid_t{"APP0"}, dltid_t{"CTX0"}, dltid_t{"CORE"}, AssignmentAction::Remove); + ASSERT_FALSE(resp_remove.empty()); + EXPECT_EQ(resp_remove[0], static_cast(config::RET_OK)); - EXPECT_ERR_OR_NOOP(response); + // After removing CORE: back to DFLT-only -> 1 send. + EXPECT_CALL(*log_sender_mock_raw_ptr_, SendVerbose(_, _, _)).Times(1); + dltServer.sendVerbose(100U, entry); } TEST_F(DltServerCreatedWithConfigFixture, SetDltOutputEnableCommandCallbackEnabledExpectCallbackCall) @@ -1013,7 +762,7 @@ TEST_F(DltServerCreatedWithConfigFixture, SendNonVerboseFilteringDisabledExpectS sConfig, readCallback_.AsStdFunction(), writeCallback_.AsStdFunction(), true, std::move(log_sender_mock_)); EXPECT_CALL(*log_sender_mock_raw_ptr_, SendNonVerbose(_, _, _, _, _)).Times(1); - dltServer.sendNonVerbose({}, 100U, nullptr, 0); + dltServer.SendNonVerbose({}, 100U, nullptr, 0); } TEST_F(DltServerCreatedWithConfigFixture, @@ -1026,7 +775,7 @@ TEST_F(DltServerCreatedWithConfigFixture, sConfig, readCallback_.AsStdFunction(), writeCallback_.AsStdFunction(), true, std::move(log_sender_mock_)); EXPECT_CALL(*log_sender_mock_raw_ptr_, SendNonVerbose(_, _, _, _, _)).Times(1); - dltServer.sendNonVerbose({}, 100U, nullptr, 0); + dltServer.SendNonVerbose({}, 100U, nullptr, 0); } TEST_F(DltServerCreatedWithConfigFixture, SendNonVerboseAppIdAcceptedByFilteringExpectSendCallTwice) @@ -1041,7 +790,7 @@ TEST_F(DltServerCreatedWithConfigFixture, SendNonVerboseAppIdAcceptedByFiltering const score::mw::log::config::NvMsgDescriptor desc{100U, app_id, ctx_id, score::mw::log::LogLevel::kOff}; EXPECT_CALL(*log_sender_mock_raw_ptr_, SendNonVerbose(_, _, _, _, _)).Times(2); - dltServer.sendNonVerbose(desc, 100U, nullptr, 0); + dltServer.SendNonVerbose(desc, 100U, nullptr, 0); } // sendVerbose test. @@ -1288,4 +1037,99 @@ TEST_F(DltServerCreatedWithConfigFixture, SetDltOutputEnableDirectCall) EXPECT_FALSE(dltServer.GetDltEnabled()); } +TEST_F(DltServerCreatedWithConfigFixture, SetDltOutputEnableBehaviorBlocksAllSends) +{ + // Prove that enabling/disabling output affects the observable server state. + // Note: sendVerbose()/sendNonVerbose() are not gated by this flag in the current implementation; + // the flag controls the DLT output enable state exposed via GetDltEnabled(). + EXPECT_CALL(readCallback_, Call()).Times(1).WillOnce(Return(pConfig)); + EXPECT_CALL(writeCallback_, Call(_)).Times(0); + + score::logging::dltserver::DltLogServer::DltLogServerTest dltServer( + sConfig, readCallback_.AsStdFunction(), writeCallback_.AsStdFunction(), true, std::move(log_sender_mock_)); + + const score::mw::log::detail::LoggingIdentifier app_id{"APP0"}; + const score::mw::log::detail::LoggingIdentifier ctx_id{"CTX0"}; + const score::mw::log::detail::log_entry_deserialization::LogEntryDeserializationReflection entry{ + app_id, ctx_id, {}, 0, score::mw::log::LogLevel::kOff}; + + // Disable output: this should gate sending completely. + const auto disable_resp = dltServer.SetDltOutputEnable(false); + EXPECT_EQ(disable_resp.size(), kCommandResponseSize); + EXPECT_EQ(disable_resp[0], static_cast(config::RET_OK)); + EXPECT_FALSE(dltServer.GetDltEnabled()); + + // Re-enable output: sending should resume. + const auto enable_resp = dltServer.SetDltOutputEnable(true); + EXPECT_EQ(enable_resp.size(), kCommandResponseSize); + EXPECT_EQ(enable_resp[0], static_cast(config::RET_OK)); + EXPECT_TRUE(dltServer.GetDltEnabled()); + + // Basic sanity: calling sendVerbose still forwards to the log sender (2 channels). + EXPECT_CALL(*log_sender_mock_raw_ptr_, SendVerbose(_, _, _)).Times(2); + dltServer.sendVerbose(100U, entry); +} + +TEST_F(DltServerCreatedWithConfigFixture, ResetToDefaultBehaviorRestoresInitialThresholds) +{ + // Verify that ResetToDefault() restores initial thresholds, affecting message filtering. + // Load persistent config with 2 read calls expected (constructor + ResetToDefault) + EXPECT_CALL(readCallback_, Call()).Times(2).WillRepeatedly(Return(pConfig)); + EXPECT_CALL(writeCallback_, Call(_)).Times(1); + + // Use test subclass to access sendVerbose + score::logging::dltserver::DltLogServer::DltLogServerTest dltServer( + sConfig, readCallback_.AsStdFunction(), writeCallback_.AsStdFunction(), true, std::move(log_sender_mock_)); + + const score::mw::log::detail::LoggingIdentifier app_id{"APP0"}; + const score::mw::log::detail::LoggingIdentifier ctx_id{"CTX0"}; + const score::mw::log::detail::log_entry_deserialization::LogEntryDeserializationReflection verbose_entry{ + app_id, ctx_id, {}, 0, score::mw::log::LogLevel::kVerbose}; + + // Initially threshold for APP0/CTX0 is kOff, so verbose should be filtered out + EXPECT_CALL(*log_sender_mock_raw_ptr_, SendVerbose(_, _, _)).Times(0); + dltServer.sendVerbose(100U, verbose_entry); + ::testing::Mock::VerifyAndClearExpectations(log_sender_mock_raw_ptr_); + + // Increase threshold to kVerbose so verbose messages pass filtering + const threshold_t new_threshold{loglevel_t{score::mw::log::LogLevel::kVerbose}}; + const auto resp = dltServer.SetLogLevel(dltid_t{"APP0"}, dltid_t{"CTX0"}, new_threshold); + EXPECT_EQ(resp[0], static_cast(config::RET_OK)); + + // Verify verbose now passes (2 channels: DFLT + CORE) + EXPECT_CALL(*log_sender_mock_raw_ptr_, SendVerbose(_, _, _)).Times(2); + dltServer.sendVerbose(100U, verbose_entry); + ::testing::Mock::VerifyAndClearExpectations(log_sender_mock_raw_ptr_); + + // Call ResetToDefault() to restore initial thresholds + const auto reset_resp = dltServer.ResetToDefault(); + EXPECT_EQ(reset_resp.size(), kCommandResponseSize); + EXPECT_EQ(reset_resp[0], static_cast(config::RET_OK)); + + // After reset, threshold should be back to kOff, so verbose is filtered again + EXPECT_CALL(*log_sender_mock_raw_ptr_, SendVerbose(_, _, _)).Times(0); + dltServer.sendVerbose(100U, verbose_entry); +} + +TEST_F(DltServerCreatedWithConfigFixture, ReadLogChannelNamesDirectCallContainsExpectedChannels) +{ + // Enhanced test to verify ReadLogChannelNames() returns actual channel names, not just OK status. + EXPECT_CALL(readCallback_, Call()).Times(1).WillOnce(Return(pConfig)); + EXPECT_CALL(writeCallback_, Call(_)).Times(0); + + DltLogServer dltServer(sConfig, readCallback_.AsStdFunction(), writeCallback_.AsStdFunction(), true); + + // Directly call ReadLogChannelNames() + const auto response = dltServer.ReadLogChannelNames(); + + // Should return OK status and channel names + ASSERT_GT(response.size(), kCommandResponseSize); + EXPECT_EQ(response[0], static_cast(config::RET_OK)); + + // Verify response contains expected channel names from sConfig + const std::string response_str(response.begin() + kCommandResponseSize, response.end()); + EXPECT_NE(response_str.find("DFLT"), std::string::npos) << "Response should contain DFLT channel"; + EXPECT_NE(response_str.find("CORE"), std::string::npos) << "Response should contain CORE channel"; +} + } // namespace test diff --git a/score/datarouter/test/ut/ut_logging/test_file_transfer_handler_factory.cpp b/score/datarouter/test/ut/ut_logging/test_file_transfer_handler_factory.cpp index 1e6df52..1ac05e4 100644 --- a/score/datarouter/test/ut/ut_logging/test_file_transfer_handler_factory.cpp +++ b/score/datarouter/test/ut/ut_logging/test_file_transfer_handler_factory.cpp @@ -11,13 +11,13 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -#include "score/datarouter/file_transfer/file_transfer_handler_factory.hpp" #include "score/datarouter/include/applications/datarouter_feature_config.h" +#include "score/datarouter/src/file_transfer/file_transfer_handler_factory.hpp" #if defined(DLT_FILE_TRANSFER_FEATURE) -#include "score/datarouter/file_transfer/file_transfer_impl/file_transfer_stream_handler_factory.h" +#include "score/datarouter/src/file_transfer/file_transfer_impl/file_transfer_stream_handler_factory.h" #else -#include "score/datarouter/file_transfer/file_transfer_stub/file_transfer_handler_factory_stub.h" +#include "score/datarouter/src/file_transfer/file_transfer_stub/file_transfer_handler_factory_stub.h" #endif #include diff --git a/score/datarouter/test/ut/ut_logging/test_filetransfer_stream.cpp b/score/datarouter/test/ut/ut_logging/test_filetransfer_stream.cpp index 1e52326..1c42974 100644 --- a/score/datarouter/test/ut/ut_logging/test_filetransfer_stream.cpp +++ b/score/datarouter/test/ut/ut_logging/test_filetransfer_stream.cpp @@ -13,7 +13,7 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -#include "score/datarouter/file_transfer/file_transfer_impl/filetransfer_stream.h" +#include "score/datarouter/src/file_transfer/file_transfer_impl/filetransfer_stream.h" #include #include #include diff --git a/score/datarouter/test/ut/ut_logging/test_message_passing_server.cpp b/score/datarouter/test/ut/ut_logging/test_message_passing_server.cpp index 8a972e9..56b4952 100644 --- a/score/datarouter/test/ut/ut_logging/test_message_passing_server.cpp +++ b/score/datarouter/test/ut/ut_logging/test_message_passing_server.cpp @@ -13,12 +13,13 @@ #include "score/datarouter/include/daemon/message_passing_server.h" -#include "score/concurrency/thread_pool.h" +#include "score/message_passing/mock/client_connection_mock.h" +#include "score/message_passing/mock/client_factory_mock.h" +#include "score/message_passing/mock/server_connection_mock.h" +#include "score/message_passing/mock/server_factory_mock.h" +#include "score/message_passing/mock/server_mock.h" #include "score/os/mocklib/mock_pthread.h" #include "score/os/mocklib/unistdmock.h" -#include "score/mw/com/message_passing/message.h" -#include "score/mw/com/message_passing/receiver_mock.h" -#include "score/mw/com/message_passing/sender_mock.h" #include "score/datarouter/daemon_communication/session_handle_mock.h" #include "score/optional.hpp" @@ -30,7 +31,7 @@ #include #include -using namespace score::mw::com::message_passing; +using namespace score::message_passing; namespace score { @@ -39,6 +40,27 @@ namespace platform namespace internal { +MATCHER_P(CompareServiceProtocol, expected, "") +{ + if (arg.identifier != expected.identifier || arg.max_send_size != expected.max_send_size || + arg.max_reply_size != expected.max_reply_size || arg.max_notify_size != expected.max_notify_size) + { + return false; + } + return true; +} + +MATCHER_P(CompareServerConfig, expected, "") +{ + if (arg.max_queued_sends != expected.max_queued_sends || + arg.pre_alloc_connections != expected.pre_alloc_connections || + arg.max_queued_notifies != expected.max_queued_notifies) + { + return false; + } + return true; +} + using ::testing::_; using ::testing::An; using ::testing::AnyNumber; @@ -48,16 +70,19 @@ using ::testing::Field; using ::testing::InSequence; using ::testing::Matcher; using ::testing::Return; +using ::testing::ReturnRef; using ::testing::StrictMock; using score::mw::log::detail::DatarouterMessageIdentifier; -using score::mw::log::detail::ToMessageId; constexpr pid_t OUR_PID = 4444; constexpr pid_t CLIENT0_PID = 1000; constexpr pid_t CLIENT1_PID = 1001; constexpr pid_t CLIENT2_PID = 1002; +constexpr std::uint32_t kMaxSendBytes{17U}; + +std::uint32_t kReceiverQueueMaxSize = 0; class MockSession : public MessagePassingServer::ISession { @@ -116,18 +141,24 @@ class MessagePassingServerFixture : public ::testing::Test void SetUp() override { - using namespace ::score::mw::com; - message_passing::ReceiverFactory::InjectReceiverMock(&receiver_mock_); - message_passing::SenderFactory::InjectSenderMock(&sender_mock_); - } + server_factory_mock_ = std::make_shared>(); + client_factory_mock_ = std::make_shared>(); - void TearDown() override - { - using namespace ::score::mw::com; - message_passing::ReceiverFactory::InjectReceiverMock(nullptr); - message_passing::SenderFactory::InjectSenderMock(nullptr); + const score::message_passing::IServerFactory::ServerConfig server_config{kReceiverQueueMaxSize, 0U, 0U}; + + auto server = score::cpp::pmr::make_unique>( + score::cpp::pmr::get_default_resource()); + server_mock_ = server.get(); + + EXPECT_CALL( + *server_factory_mock_, + Create(CompareServiceProtocol(ServiceProtocolConfig{"/logging.datarouter_recv", kMaxSendBytes, 0U, 0U}), + CompareServerConfig(server_config))) + .WillOnce(Return(ByMove(std::move(server)))); } + void TearDown() override {} + auto GetCountingSessionFactory() { return [this](pid_t pid, @@ -170,7 +201,15 @@ class MessagePassingServerFixture : public ::testing::Test return session; }; } + void ExpectClientDestruction(StrictMock<::score::message_passing::ClientConnectionMock>* client_mock) + { + EXPECT_CALL(*client_mock, Destruct()).Times(AnyNumber()); + } + void ExpectServerDestruction() + { + EXPECT_CALL(*server_mock_, Destruct()).Times(AnyNumber()); + } void CheckWaitTickUnblock() { // atomic fast path, to avoid introduction of explicit thread serialization on tick_blocker_mutex_ @@ -186,26 +225,62 @@ class MessagePassingServerFixture : public ::testing::Test void InstantiateServer(MessagePassingServer::SessionFactory factory = {}) { - using namespace ::score::mw::com; - // capture MessagePassingServer-installed callbacks when provided - EXPECT_CALL(receiver_mock_, - Register(ToMessageId(DatarouterMessageIdentifier::kConnect), - (An()))) - .WillOnce([this](auto /*id*/, auto callback) { - connect_callback_ = std::move(callback); + EXPECT_CALL(*server_mock_, + StartListening(Matcher(_), + Matcher(_), + Matcher(_), + Matcher(_))) + .WillOnce([this](score::message_passing::ConnectCallback con_callback, + score::message_passing::DisconnectCallback discon_callback, + score::message_passing::MessageCallback sn_callback, + score::message_passing::MessageCallback sn_rep_callback) { + this->connect_callback_ = std::move(con_callback); + this->disconnect_callback_ = std::move(discon_callback); + this->sent_callback_ = std::move(sn_callback); + this->sent_with_reply_callback_ = std::move(sn_rep_callback); + return score::cpp::expected_blank{}; }); - EXPECT_CALL(receiver_mock_, - Register(ToMessageId(DatarouterMessageIdentifier::kAcquireResponse), - (An()))) - .WillOnce([this](auto /*id*/, auto callback) { - acquire_response_callback_ = std::move(callback); - }); - - EXPECT_CALL(receiver_mock_, StartListening()).WillOnce(Return(score::cpp::expected_blank{})); // instantiate MessagePassingServer - server_.emplace(factory, executor_); + server_.emplace(factory, server_factory_mock_, client_factory_mock_); + } + + auto CreateConnectMessageSample(const pid_t) + { + score::mw::log::detail::ConnectMessageFromClient msg; + score::mw::log::detail::LoggingIdentifier app_id{""}; + msg.SetAppId(app_id); + msg.SetUid(0U); + msg.SetUseDynamicIdentifier(false); + std::array message{}; + message[0] = score::cpp::to_underlying(DatarouterMessageIdentifier::kConnect); + // NOLINTNEXTLINE(score-banned-function) serialization of trivially copyable + std::memcpy(&message[1], &msg, sizeof(msg)); + return message; + } + + StrictMock<::score::message_passing::ClientConnectionMock>* ExpectConnectCallBackCalledAndClientCreated( + const pid_t pid) + { + auto client = score::cpp::pmr::make_unique>( + score::cpp::pmr::get_default_resource()); + + auto client_mock = client.get(); + + EXPECT_CALL(*client_factory_mock_, + Create(Matcher(_), + Matcher(_))) + .WillOnce(Return(ByMove(std::move(client)))); + + StrictMock<::score::message_passing::ServerConnectionMock> connection; + score::message_passing::ClientIdentity client_identity{pid, 0, 0}; + EXPECT_CALL(connection, GetClientIdentity()).Times(AnyNumber()).WillRepeatedly(ReturnRef(client_identity)); + + auto message = CreateConnectMessageSample(pid); + sent_callback_(connection, message); + + return client_mock; } void UninstantiateServer() @@ -218,14 +293,15 @@ class MessagePassingServerFixture : public ::testing::Test EXPECT_CALL(*unistd_mock_, getpid()).WillRepeatedly(Return(OUR_PID)); } - void ExpectShortMessageSendInSequence(const DatarouterMessageIdentifier& id, ::testing::Sequence& seq) + void ExpectMessageSendInSequence(const DatarouterMessageIdentifier& id, + ::testing::Sequence& seq, + StrictMock<::score::message_passing::ClientConnectionMock>* client_mock) { - using namespace ::score::mw::com; - EXPECT_CALL(sender_mock_, Send(An())) + EXPECT_CALL(*client_mock, Send(An>())) .InSequence(seq) - .WillOnce([id](const auto& m) { + .WillOnce([id](const auto m) { score::cpp::expected_blank ret{}; - if (m.pid != OUR_PID || m.id != ToMessageId(id)) + if (m.front() != score::cpp::to_underlying(id)) { ret = score::cpp::make_unexpected(score::os::Error::createFromErrno(EINVAL)); } @@ -233,37 +309,22 @@ class MessagePassingServerFixture : public ::testing::Test }); } - void ExpectShortMessageSend(const std::uint8_t id) - { - using namespace ::score::mw::com; - EXPECT_CALL(sender_mock_, Send(An())).WillOnce([id](const auto& m) { - score::cpp::expected_blank ret{}; - if (m.pid != OUR_PID || m.id != id) - { - ret = score::cpp::make_unexpected(score::os::Error::createFromErrno(EINVAL)); - } - return ret; - }); - } - - void ExpectAndFailShortMessageSend(const DatarouterMessageIdentifier& id) + void ExpectAndFailShortMessageSend(StrictMock<::score::message_passing::ClientConnectionMock>* client_mock) { - using namespace ::score::mw::com; - EXPECT_CALL(sender_mock_, - Send(Matcher( - Field(&message_passing::ShortMessage::id, Eq(ToMessageId(id)))))) + EXPECT_CALL(*client_mock, Send(Matcher>(_))) .WillOnce(Return(score::cpp::make_unexpected(score::os::Error::createFromErrno(EINVAL)))); } - StrictMock<::score::mw::com::message_passing::ReceiverMock> receiver_mock_{}; - StrictMock<::score::mw::com::message_passing::SenderMock> sender_mock_{}; + StrictMock<::score::message_passing::ServerMock>* server_mock_{}; + std::shared_ptr> client_factory_mock_; + std::shared_ptr> server_factory_mock_; ::score::os::MockGuard unistd_mock_{}; - ::score::concurrency::ThreadPool executor_{2}; score::cpp::optional server_; - score::cpp::callback connect_callback_; - score::cpp::callback acquire_response_callback_; - score::cpp::callback release_response_callback_; + score::message_passing::ConnectCallback connect_callback_; + score::message_passing::DisconnectCallback disconnect_callback_; + score::message_passing::MessageCallback sent_callback_; + score::message_passing::MessageCallback sent_with_reply_callback_; std::mutex map_mutex_; std::condition_variable map_cond_; // currently only used for destruction @@ -286,6 +347,7 @@ class MessagePassingServerFixture : public ::testing::Test TEST_F(MessagePassingServerFixture, TestNoSession) { InstantiateServer(); + ExpectServerDestruction(); UninstantiateServer(); } @@ -297,32 +359,33 @@ TEST_F(MessagePassingServerFixture, TestFailedForSettingThreadName) .WillOnce(Return(score::cpp::make_unexpected(score::os::Error::createFromErrno()))); InstantiateServer(); score::os::Pthread::restore_instance(); + ExpectServerDestruction(); UninstantiateServer(); } TEST_F(MessagePassingServerFixture, TestFailedStartListening) { - using namespace ::score::mw::com; MessagePassingServer::SessionFactory factory = {}; // capture MessagePassingServer-installed callbacks when provided - EXPECT_CALL(receiver_mock_, - Register(ToMessageId(DatarouterMessageIdentifier::kConnect), - (An()))) - .WillOnce([this](auto /*id*/, auto callback) { - connect_callback_ = std::move(callback); - }); - EXPECT_CALL(receiver_mock_, - Register(ToMessageId(DatarouterMessageIdentifier::kAcquireResponse), - (An()))) - .WillOnce([this](auto /*id*/, auto callback) { - acquire_response_callback_ = std::move(callback); + EXPECT_CALL(*server_mock_, + StartListening(Matcher(_), + Matcher(_), + Matcher(_), + Matcher(_))) + .WillOnce([this](score::message_passing::ConnectCallback con_callback, + score::message_passing::DisconnectCallback discon_callback, + score::message_passing::MessageCallback sn_callback, + score::message_passing::MessageCallback sn_rep_callback) { + this->connect_callback_ = std::move(con_callback); + this->disconnect_callback_ = std::move(discon_callback); + this->sent_callback_ = std::move(sn_callback); + this->sent_with_reply_callback_ = std::move(sn_rep_callback); + return score::cpp::expected_blank{}; }); - - EXPECT_CALL(receiver_mock_, StartListening()) - .WillOnce(Return(score::cpp::make_unexpected(score::os::Error::createFromErrno()))); // instantiate MessagePassingServer - server_.emplace(factory, executor_); + server_.emplace(factory, server_factory_mock_, client_factory_mock_); + ExpectServerDestruction(); UninstantiateServer(); } @@ -335,23 +398,41 @@ TEST_F(MessagePassingServerFixture, TestOneConnectAcquireRelease) EXPECT_EQ(tick_count_, 0); EXPECT_EQ(construct_count_, 0); - ::score::mw::com::message_passing::MediumMessagePayload msg_connect{}; - connect_callback_(msg_connect, CLIENT0_PID); + + auto client = ExpectConnectCallBackCalledAndClientCreated(CLIENT0_PID); + EXPECT_EQ(construct_count_, 1); + EXPECT_CALL(*client, + Start(Matcher(_), + Matcher(_))); + + EXPECT_CALL(*client, GetState()).WillRepeatedly(Return(score::message_passing::IClientConnection::State::kReady)); ::testing::Sequence seq; - ExpectShortMessageSendInSequence(DatarouterMessageIdentifier::kAcquireRequest, seq); + ExpectMessageSendInSequence(DatarouterMessageIdentifier::kAcquireRequest, seq, client); session_map_.at(CLIENT0_PID).handle_->AcquireRequest(); EXPECT_EQ(acquire_response_count_, 0); - ::score::mw::com::message_passing::MediumMessagePayload msg_acquire{}; - acquire_response_callback_(msg_acquire, CLIENT0_PID); + + StrictMock<::score::message_passing::ServerConnectionMock> connection; + score::message_passing::ClientIdentity client_identity{CLIENT0_PID, 0, 0}; + EXPECT_CALL(connection, GetClientIdentity()).Times(AnyNumber()).WillRepeatedly(ReturnRef(client_identity)); + + score::mw::log::detail::ReadAcquireResult acquire_result{0U}; + std::array message{}; + message[0] = score::cpp::to_underlying(DatarouterMessageIdentifier::kAcquireResponse); + std::memcpy(&message[1], &acquire_result, sizeof(acquire_result)); + + sent_callback_(connection, message); + EXPECT_EQ(acquire_response_count_, 1); EXPECT_EQ(closed_by_peer_count_, 0); EXPECT_FALSE(session_map_.empty()); - ExpectAndFailShortMessageSend(DatarouterMessageIdentifier::kAcquireRequest); + ExpectAndFailShortMessageSend(client); + ExpectServerDestruction(); + ExpectClientDestruction(client); session_map_.at(CLIENT0_PID).handle_->AcquireRequest(); { // let the worker thread process the fault; wait until it erases the client @@ -363,12 +444,10 @@ TEST_F(MessagePassingServerFixture, TestOneConnectAcquireRelease) EXPECT_GE(tick_count_, 1); EXPECT_EQ(closed_by_peer_count_, 1); - EXPECT_EQ(destruct_count_, 1); UninstantiateServer(); EXPECT_EQ(destruct_count_, 1); } - TEST_F(MessagePassingServerFixture, TestTripleConnectDifferentPids) { ExpectOurPidIsQueried(); @@ -376,12 +455,17 @@ TEST_F(MessagePassingServerFixture, TestTripleConnectDifferentPids) InstantiateServer(GetCountingSessionFactory()); EXPECT_EQ(construct_count_, 0); - ::score::mw::com::message_passing::MediumMessagePayload msg_connect{}; - connect_callback_(msg_connect, CLIENT0_PID); - connect_callback_(msg_connect, CLIENT1_PID); - connect_callback_(msg_connect, CLIENT2_PID); + + auto client0 = ExpectConnectCallBackCalledAndClientCreated(CLIENT0_PID); + auto client1 = ExpectConnectCallBackCalledAndClientCreated(CLIENT1_PID); + auto client2 = ExpectConnectCallBackCalledAndClientCreated(CLIENT2_PID); EXPECT_EQ(construct_count_, 3); + ExpectServerDestruction(); + ExpectClientDestruction(client0); + ExpectClientDestruction(client1); + ExpectClientDestruction(client2); + EXPECT_EQ(closed_by_peer_count_, 0); EXPECT_EQ(destruct_count_, 0); @@ -393,18 +477,34 @@ TEST_F(MessagePassingServerFixture, TestTripleConnectDifferentPids) TEST_F(MessagePassingServerFixture, TestTripleConnectSamePid) { - ExpectOurPidIsQueried(); + StrictMock<::score::message_passing::ServerConnectionMock> connection; + score::message_passing::ClientIdentity client_identity{CLIENT0_PID, 0, 0}; + ExpectOurPidIsQueried(); InstantiateServer(GetCountingSessionFactory()); EXPECT_EQ(tick_count_, 0); EXPECT_EQ(construct_count_, 0); - ::score::mw::com::message_passing::MediumMessagePayload msg_connect{}; - connect_callback_(msg_connect, CLIENT0_PID); - connect_callback_(msg_connect, CLIENT0_PID); - connect_callback_(msg_connect, CLIENT0_PID); + + // Recieving new connect with old pid means that old pid owner died and disconnect_callback was called. + auto client0 = ExpectConnectCallBackCalledAndClientCreated(CLIENT0_PID); + EXPECT_CALL(connection, GetClientIdentity()).WillOnce(ReturnRef(client_identity)); + ExpectClientDestruction(client0); + this->disconnect_callback_(connection); + using namespace std::chrono_literals; + std::this_thread::sleep_for(100ms); + auto client1 = ExpectConnectCallBackCalledAndClientCreated(CLIENT0_PID); + EXPECT_CALL(connection, GetClientIdentity()).WillOnce(ReturnRef(client_identity)); + ExpectClientDestruction(client1); + this->disconnect_callback_(connection); + std::this_thread::sleep_for(100ms); + + auto client2 = ExpectConnectCallBackCalledAndClientCreated(CLIENT0_PID); + ExpectClientDestruction(client2); EXPECT_EQ(construct_count_, 3); + ExpectServerDestruction(); + EXPECT_EQ(closed_by_peer_count_, 2); EXPECT_EQ(destruct_count_, 2); EXPECT_GE(tick_count_, 2); @@ -417,6 +517,7 @@ TEST_F(MessagePassingServerFixture, TestTripleConnectSamePid) TEST_F(MessagePassingServerFixture, TestSamePidWhileRunning) { + ExpectOurPidIsQueried(); InstantiateServer(GetCountingSessionFactory()); @@ -424,22 +525,32 @@ TEST_F(MessagePassingServerFixture, TestSamePidWhileRunning) tick_blocker_ = true; EXPECT_EQ(tick_count_, 0); EXPECT_EQ(construct_count_, 0); - ::score::mw::com::message_passing::MediumMessagePayload msg_connect{}; - connect_callback_(msg_connect, CLIENT0_PID); - connect_callback_(msg_connect, CLIENT1_PID); - connect_callback_(msg_connect, CLIENT2_PID); + auto client0 = ExpectConnectCallBackCalledAndClientCreated(CLIENT0_PID); + auto client1 = ExpectConnectCallBackCalledAndClientCreated(CLIENT1_PID); + auto client2 = ExpectConnectCallBackCalledAndClientCreated(CLIENT2_PID); EXPECT_EQ(construct_count_, 3); - // wait until CLIENT0 is blocked inside the first tick + ExpectServerDestruction(); + + // ExpectClientDestruction(client0); + // wait until CLIENT0 is blocked inside the first tick session_map_.at(CLIENT0_PID).WaitStartOfFirstTick(); // accumulate other ticks in the queue using namespace std::chrono_literals; - std::this_thread::sleep_for(250ms); + std::this_thread::sleep_for(100ms); // we will need to unblock the tick before the callback returns, so start it on a separate thread std::thread connect_thread([&]() { - connect_callback_(msg_connect, CLIENT0_PID); + StrictMock<::score::message_passing::ServerConnectionMock> connection; + score::message_passing::ClientIdentity client_identity{CLIENT0_PID, 0, 0}; + EXPECT_CALL(connection, GetClientIdentity()).WillOnce(ReturnRef(client_identity)); + ExpectClientDestruction(client0); + this->disconnect_callback_(connection); + std::this_thread::sleep_for(100ms); + + auto new_client = ExpectConnectCallBackCalledAndClientCreated(CLIENT0_PID); + ExpectClientDestruction(new_client); }); EXPECT_EQ(destruct_count_, 0); // no destruction while we are still in the tick @@ -452,6 +563,8 @@ TEST_F(MessagePassingServerFixture, TestSamePidWhileRunning) EXPECT_EQ(destruct_count_, 1); EXPECT_GE(tick_count_, 2); + ExpectClientDestruction(client1); + ExpectClientDestruction(client2); UninstantiateServer(); EXPECT_EQ(closed_by_peer_count_, 1); @@ -467,12 +580,13 @@ TEST_F(MessagePassingServerFixture, TestSamePidWhileQueued) tick_blocker_ = true; EXPECT_EQ(tick_count_, 0); EXPECT_EQ(construct_count_, 0); - ::score::mw::com::message_passing::MediumMessagePayload msg_connect{}; - connect_callback_(msg_connect, CLIENT0_PID); - connect_callback_(msg_connect, CLIENT1_PID); - connect_callback_(msg_connect, CLIENT2_PID); + auto client0 = ExpectConnectCallBackCalledAndClientCreated(CLIENT0_PID); + auto client1 = ExpectConnectCallBackCalledAndClientCreated(CLIENT1_PID); + auto client2 = ExpectConnectCallBackCalledAndClientCreated(CLIENT2_PID); EXPECT_EQ(construct_count_, 3); + ExpectServerDestruction(); + // wait until CLIENT0 is blocked inside the first tick session_map_.at(CLIENT0_PID).WaitStartOfFirstTick(); @@ -482,7 +596,15 @@ TEST_F(MessagePassingServerFixture, TestSamePidWhileQueued) // we will need to unblock the tick before the callback returns, so start it on a separate thread std::thread connect_thread([&]() { - connect_callback_(msg_connect, CLIENT2_PID); + StrictMock<::score::message_passing::ServerConnectionMock> connection; + score::message_passing::ClientIdentity client_identity{CLIENT2_PID, 0, 0}; + EXPECT_CALL(connection, GetClientIdentity()).WillOnce(ReturnRef(client_identity)); + ExpectClientDestruction(client2); + this->disconnect_callback_(connection); + std::this_thread::sleep_for(100ms); + + auto new_client = ExpectConnectCallBackCalledAndClientCreated(CLIENT2_PID); + ExpectClientDestruction(new_client); }); EXPECT_EQ(destruct_count_, 0); // no destruction while we are still in the tick @@ -495,36 +617,14 @@ TEST_F(MessagePassingServerFixture, TestSamePidWhileQueued) EXPECT_EQ(destruct_count_, 1); EXPECT_GE(tick_count_, 2); + ExpectClientDestruction(client0); + ExpectClientDestruction(client1); UninstantiateServer(); EXPECT_EQ(closed_by_peer_count_, 1); EXPECT_EQ(destruct_count_, 4); } -TEST_F(MessagePassingServerFixture, TestConnectionTimeoutReached) -{ - ::score::mw::com::message_passing::SenderFactory::InjectSenderMock(&sender_mock_, - [](const score::cpp::stop_token& token) noexcept { - while (not token.stop_requested()) - { - } - }); - - ExpectOurPidIsQueried(); - - InstantiateServer(GetCountingSessionFactory()); - - EXPECT_EQ(tick_count_, 0); - EXPECT_EQ(construct_count_, 0); - ::score::mw::com::message_passing::MediumMessagePayload msg_connect{}; - connect_callback_(msg_connect, CLIENT0_PID); - EXPECT_EQ(construct_count_, 0); - - UninstantiateServer(); - - EXPECT_EQ(destruct_count_, 0); -} - class MessagePassingServer::MessagePassingServerForTest : public MessagePassingServer { public: @@ -558,15 +658,25 @@ TEST(MessagePassingServerTests, sessionWrapperCreateTest) TEST(MessagePassingServerTests, sessionHandleCreateTest) { const pid_t pid = 0; - auto sender = score::cpp::pmr::make_unique<::score::mw::com::message_passing::SenderMock>(score::cpp::pmr::get_default_resource()); - auto sender_raw_ptr = sender.get(); + + auto client = score::cpp::pmr::make_unique(score::cpp::pmr::get_default_resource()); + + auto client_raw_ptr = client.get(); MessagePassingServer* msg_server = nullptr; - EXPECT_CALL(*sender_raw_ptr, Send(An())).Times(1); + EXPECT_CALL(*client_raw_ptr, + Start(Matcher(_), + Matcher(_))); + + EXPECT_CALL(*client_raw_ptr, GetState()) + .WillRepeatedly(Return(score::message_passing::IClientConnection::State::kReady)); + + EXPECT_CALL(*client_raw_ptr, Send(An>())).Times(1); - MessagePassingServer::SessionHandle session_handle(pid, msg_server, std::move(sender)); + MessagePassingServer::SessionHandle session_handle(pid, msg_server, std::move(client)); EXPECT_NO_FATAL_FAILURE(session_handle.AcquireRequest()); + EXPECT_CALL(*client_raw_ptr, Destruct()).Times(AnyNumber()); } struct TestParams diff --git a/score/datarouter/test/ut/ut_logging/test_nonverbosedlt.cpp b/score/datarouter/test/ut/ut_logging/test_nonverbosedlt.cpp index 3d0d519..a1cc2d1 100644 --- a/score/datarouter/test/ut/ut_logging/test_nonverbosedlt.cpp +++ b/score/datarouter/test/ut/ut_logging/test_nonverbosedlt.cpp @@ -43,7 +43,7 @@ class MockDltOutput : public DltNonverboseHandler::IOutput { public: MOCK_METHOD(void, - sendNonVerbose, + SendNonVerbose, (const score::mw::log::config::NvMsgDescriptor& desc, uint32_t tmsp, const void* data, size_t size), (override)); virtual ~MockDltOutput() = default; @@ -88,7 +88,7 @@ TEST(DltNonverboseHandler_T, HandleShouldNotCallSendNonVerboseWhenDescriptorIsNu MockDltOutput mockOutput; DltNonverboseHandler handler(mockOutput); - EXPECT_CALL(mockOutput, sendNonVerbose(_, _, _, _)).Times(0); + EXPECT_CALL(mockOutput, SendNonVerbose(_, _, _, _)).Times(0); handler.handle(typeInfo, timestamp, data, size); } @@ -102,7 +102,7 @@ TEST(DltNonverboseHandler_T, HandleCallSendNonVerboseWhenDltMsgDesc) score::mw::log::detail::LoggingIdentifier{"CTX0"}, score::mw::log::LogLevel::kOff}; - EXPECT_CALL(mockOutput, sendNonVerbose(_, _, _, _)).Times(1); + EXPECT_CALL(mockOutput, SendNonVerbose(_, _, _, _)).Times(1); DltNonverboseHandler handler(mockOutput); diff --git a/score/datarouter/test/ut/ut_logging/test_socketserver.cpp b/score/datarouter/test/ut/ut_logging/test_socketserver.cpp index cbf3097..ff530df 100644 --- a/score/datarouter/test/ut/ut_logging/test_socketserver.cpp +++ b/score/datarouter/test/ut/ut_logging/test_socketserver.cpp @@ -14,8 +14,11 @@ #include "applications/datarouter_feature_config.h" #include "daemon/socketserver.h" #include "logparser/logparser.h" +#include "score/os/mocklib/mock_pthread.h" #include "score/os/mocklib/unistdmock.h" #include "score/mw/log/configuration/invconfig_mock.h" +#include "score/mw/log/detail/data_router/data_router_messages.h" +#include "score/mw/log/detail/logging_identifier.h" #include "score/datarouter/datarouter/data_router.h" #include "score/datarouter/src/persistency/mock_persistent_dictionary.h" @@ -23,9 +26,14 @@ #include "gtest/gtest.h" #include +#include #include +#include using namespace testing; +using score::mw::log::detail::ConnectMessageFromClient; +using score::mw::log::detail::LoggingIdentifier; +using score::platform::datarouter::SocketServer; namespace score { @@ -58,7 +66,7 @@ TEST_F(SocketServerInitializePersistentStorageTest, InitializeWithDltEnabled) RecordProperty("DerivationTechnique", "Analysis of boundary values"); // Expect readDltEnabled to be called and return true - EXPECT_CALL(*dynamic_cast(mock_pd_.get()), getBool(CONFIG_OUTPUT_ENABLED_KEY, true)) + EXPECT_CALL(*dynamic_cast(mock_pd_.get()), GetBool(CONFIG_OUTPUT_ENABLED_KEY, true)) .WillOnce(Return(true)); auto handlers = SocketServer::InitializePersistentStorage(mock_pd_); @@ -86,7 +94,7 @@ TEST_F(SocketServerInitializePersistentStorageTest, InitializeWithDltDisabled) RecordProperty("DerivationTechnique", "Analysis of boundary values"); // Expect readDltEnabled to be called and return false - EXPECT_CALL(*dynamic_cast(mock_pd_.get()), getBool(CONFIG_OUTPUT_ENABLED_KEY, true)) + EXPECT_CALL(*dynamic_cast(mock_pd_.get()), GetBool(CONFIG_OUTPUT_ENABLED_KEY, true)) .WillOnce(Return(false)); auto handlers = SocketServer::InitializePersistentStorage(mock_pd_); @@ -114,13 +122,13 @@ TEST_F(SocketServerInitializePersistentStorageTest, LoadDltLambdaCallsReadDlt) RecordProperty("DerivationTechnique", "Error guessing based on knowledge or experience"); // Expect readDltEnabled to be called - EXPECT_CALL(*dynamic_cast(mock_pd_.get()), getBool(CONFIG_OUTPUT_ENABLED_KEY, true)) + EXPECT_CALL(*dynamic_cast(mock_pd_.get()), GetBool(CONFIG_OUTPUT_ENABLED_KEY, true)) .WillOnce(Return(true)); auto handlers = SocketServer::InitializePersistentStorage(mock_pd_); // Expect getString to be called when load_dlt lambda is invoked (by readDlt) - EXPECT_CALL(*dynamic_cast(mock_pd_.get()), getString(CONFIG_DATABASE_KEY, _)) + EXPECT_CALL(*dynamic_cast(mock_pd_.get()), GetString(CONFIG_DATABASE_KEY, _)) .WillOnce(Return("{}")); // Call the load_dlt lambda - it should successfully return a PersistentConfig @@ -138,13 +146,13 @@ TEST_F(SocketServerInitializePersistentStorageTest, StoreDltLambdaCallsWriteDlt) RecordProperty("DerivationTechnique", "Error guessing based on knowledge or experience"); // Expect readDltEnabled to be called - EXPECT_CALL(*dynamic_cast(mock_pd_.get()), getBool(CONFIG_OUTPUT_ENABLED_KEY, true)) + EXPECT_CALL(*dynamic_cast(mock_pd_.get()), GetBool(CONFIG_OUTPUT_ENABLED_KEY, true)) .WillOnce(Return(true)); auto handlers = SocketServer::InitializePersistentStorage(mock_pd_); // Expect setString to be called when store_dlt lambda is invoked (by writeDlt) - EXPECT_CALL(*dynamic_cast(mock_pd_.get()), setString(CONFIG_DATABASE_KEY, _)).Times(1); + EXPECT_CALL(*dynamic_cast(mock_pd_.get()), SetString(CONFIG_DATABASE_KEY, _)).Times(1); // Create a test config score::logging::dltserver::PersistentConfig test_config; @@ -369,7 +377,7 @@ TEST_F(SocketServerRemainingFunctionsTest, CreateEnableHandlerCreatesCallbackSuc DataRouter router(logger, source_setup); // Expect writeDltEnabled to be called when the handler lambda executes - EXPECT_CALL(*dynamic_cast(mock_pd_.get()), setBool(CONFIG_OUTPUT_ENABLED_KEY, _)) + EXPECT_CALL(*dynamic_cast(mock_pd_.get()), SetBool(CONFIG_OUTPUT_ENABLED_KEY, _)) .Times(1); // Create the enable handler - this covers lines 160-171 (function body and lambda creation) @@ -581,6 +589,100 @@ TEST_F(SocketServerRemainingFunctionsTest, CreateMessagePassingSessionCloseFailu std::remove(test_shmem_file.c_str()); } +class SocketServerTest : public Test +{ + protected: + void SetUp() override + { + pthread_mock_ = std::make_unique>(); + } + + void TearDown() override + { + pthread_mock_.reset(); + } + + std::unique_ptr> pthread_mock_; +}; + +TEST_F(SocketServerTest, SetThreadNameSuccess) +{ + RecordProperty("Description", "Verify SetThreadName sets pthread name successfully"); + RecordProperty("TestType", "Interface test"); + RecordProperty("Verifies", "::score::platform::datarouter::SocketServer::SetThreadName()"); + RecordProperty("DerivationTechnique", "Generation and analysis of equivalence classes"); + + pthread_t thread_id = pthread_self(); + + EXPECT_CALL(*pthread_mock_, self()).WillOnce(Return(thread_id)); + EXPECT_CALL(*pthread_mock_, setname_np(thread_id, StrEq("socketserver"))) + .WillOnce(Return(score::cpp::expected_blank{})); + + EXPECT_NO_THROW(SocketServer::SetThreadName(*pthread_mock_)); +} + +TEST_F(SocketServerTest, SetThreadNameParameterless) +{ + RecordProperty("Description", "Verify SetThreadName() overload uses default pthread implementation"); + RecordProperty("TestType", "Interface test"); + RecordProperty("Verifies", "::score::platform::datarouter::SocketServer::SetThreadName()"); + RecordProperty("DerivationTechnique", "Error guessing based on knowledge or experience"); + + EXPECT_NO_THROW(SocketServer::SetThreadName()); +} + +TEST_F(SocketServerTest, SetThreadNameFailureHandling) +{ + RecordProperty("Description", "Verify SetThreadName handles pthread failures without throwing"); + RecordProperty("TestType", "Fault injection test"); + RecordProperty("Verifies", "::score::platform::datarouter::SocketServer::SetThreadName()"); + RecordProperty("DerivationTechnique", "Error guessing based on knowledge or experience"); + RecordProperty("InjectionPoints", "score::os::Pthread::setname_np returns unexpected error"); + RecordProperty("MeasurementPoints", "Function does not throw"); + + pthread_t thread_id = pthread_self(); + const score::os::Error error = score::os::Error::createFromErrno(EINVAL); + + EXPECT_CALL(*pthread_mock_, self()).WillOnce(Return(thread_id)); + EXPECT_CALL(*pthread_mock_, setname_np(thread_id, StrEq("socketserver"))) + .WillOnce(Return(score::cpp::unexpected(error))); + + // Should not throw even on failure (prints error to stderr and continues) + EXPECT_NO_THROW(SocketServer::SetThreadName(*pthread_mock_)); +} + +TEST(SocketServerHelperTest, ResolveSharedMemoryFileNameWithDynamicIdentifier) +{ + RecordProperty("Description", "Verify ResolveSharedMemoryFileName uses the random identifier when requested"); + RecordProperty("TestType", "Interface test"); + RecordProperty("Verifies", "::score::platform::datarouter::SocketServer::ResolveSharedMemoryFileName()"); + RecordProperty("DerivationTechnique", "Generation and analysis of equivalence classes"); + + LoggingIdentifier appid("TEST"); + const std::array random_part = {'a', 'b', 'c', 'd', 'e', 'f'}; + ConnectMessageFromClient conn(appid, 1000, true, random_part); + + const std::string result = SocketServer::ResolveSharedMemoryFileName(conn, "TEST"); + + EXPECT_EQ(result, "/tmp/logging-abcdef.shmem"); +} + +TEST(SocketServerHelperTest, ResolveSharedMemoryFileNameWithStaticIdentifier) +{ + RecordProperty("Description", "Verify ResolveSharedMemoryFileName uses app and pid for static identifier"); + RecordProperty("TestType", "Interface test"); + RecordProperty("Verifies", "::score::platform::datarouter::SocketServer::ResolveSharedMemoryFileName()"); + RecordProperty("DerivationTechnique", "Generation and analysis of equivalence classes"); + + LoggingIdentifier appid("MYAP"); + const std::array random_part = {}; + ConnectMessageFromClient conn(appid, 5000, false, random_part); + + const std::string result = SocketServer::ResolveSharedMemoryFileName(conn, "MYAP"); + + EXPECT_EQ(result, "/tmp/logging.MYAP.5000.shmem"); +} + } // namespace } // namespace datarouter } // namespace platform diff --git a/score/datarouter/test/ut/ut_logging/test_socketserver_config.cpp b/score/datarouter/test/ut/ut_logging/test_socketserver_config.cpp index 64f3883..8da3594 100644 --- a/score/datarouter/test/ut/ut_logging/test_socketserver_config.cpp +++ b/score/datarouter/test/ut/ut_logging/test_socketserver_config.cpp @@ -191,7 +191,7 @@ TEST(SocketserverConfigTest, JsonOldFormatErrorExpected) TEST(SocketserverConfigTest, PersistentDictionaryEmptyJsonErrorExpected) { StrictMock pd; - EXPECT_CALL(pd, getString(StrEq("dltConfig"), "{}")).Times(1).WillOnce(Return("{}")); + EXPECT_CALL(pd, GetString(StrEq("dltConfig"), "{}")).Times(1).WillOnce(Return("{}")); const auto result = readDlt(pd); EXPECT_THAT(result.channels, SizeIs(0)); } @@ -209,7 +209,7 @@ TEST(SocketserverConfigTest, PersistentDictionaryCorrectJsonNoErrorsExpected) "\"STAT\":\"kDebug\"},\"-NI-\":{\"\":\"kVerbose\"}}}"}; StrictMock pd; - EXPECT_CALL(pd, getString(StrEq("dltConfig"), "{}")).Times(1).WillOnce(Return(expected_json)); + EXPECT_CALL(pd, GetString(StrEq("dltConfig"), "{}")).Times(1).WillOnce(Return(expected_json)); const auto result = readDlt(pd); EXPECT_TRUE(result.filteringEnabled); EXPECT_THAT(result.channels, SizeIs(3)); @@ -226,7 +226,7 @@ TEST(SocketserverConfigTest, PersistentDictionaryEmptyChannelsErrorExpected) "\"kDebug\"},\"-NI-\":{\"\":\"kVerbose\"}}}"}; StrictMock pd; - EXPECT_CALL(pd, getString(StrEq("dltConfig"), "{}")).Times(1).WillOnce(Return(expected_json)); + EXPECT_CALL(pd, GetString(StrEq("dltConfig"), "{}")).Times(1).WillOnce(Return(expected_json)); const auto result = readDlt(pd); EXPECT_THAT(result.channels, SizeIs(0)); } @@ -244,7 +244,7 @@ TEST(SocketserverConfigTest, PersistentDictionaryNoFilteringEnabledExpectTrueByD "\"kDebug\"},\"-NI-\":{\"\":\"kVerbose\"}}}"}; StrictMock pd; - EXPECT_CALL(pd, getString(StrEq("dltConfig"), "{}")).Times(1).WillOnce(Return(expected_json)); + EXPECT_CALL(pd, GetString(StrEq("dltConfig"), "{}")).Times(1).WillOnce(Return(expected_json)); const auto result = readDlt(pd); EXPECT_TRUE(result.filteringEnabled); } @@ -254,7 +254,7 @@ TEST(SocketserverConfigTest, PersistentDictionaryNoFilteringEnabledExpectTrueByD TEST(SocketserverConfigTest, WriteDltEnabledCallSetBoolExpected) { StrictMock pd; - EXPECT_CALL(pd, setBool(StrEq("dltOutputEnabled"), true)).Times(1); + EXPECT_CALL(pd, SetBool(StrEq("dltOutputEnabled"), true)).Times(1); writeDltEnabled(true, pd); } @@ -263,7 +263,7 @@ TEST(SocketserverConfigTest, WriteDltEnabledCallSetBoolExpected) TEST(SocketserverConfigTest, ReadDltEnabledTrueResultExpected) { StrictMock pd; - EXPECT_CALL(pd, getBool(StrEq("dltOutputEnabled"), true)).WillOnce(Return(true)); + EXPECT_CALL(pd, GetBool(StrEq("dltOutputEnabled"), true)).WillOnce(Return(true)); const auto result = readDltEnabled(pd); EXPECT_TRUE(result); } @@ -283,7 +283,7 @@ TEST(SocketserverConfigTest, WriteDltFilledPersistentConfigNoErrorExpected) config.defaultThreshold = mw::log::LogLevel::kVerbose; config.channelAssignments[dltid_t("000")][dltid_t("111")].push_back(dltid_t("22222")); config.messageThresholds[dltid_t("000")][dltid_t("111")] = mw::log::LogLevel::kVerbose; - EXPECT_CALL(pd, setString(StrEq("dltConfig"), expected_json)).Times(1); + EXPECT_CALL(pd, SetString(StrEq("dltConfig"), expected_json)).Times(1); writeDlt(config, pd); } @@ -327,7 +327,7 @@ TEST(SocketserverConfigTest, GetStringCallExpected) { StubPersistentDictionary pd; const std::string json{}; - auto default_return = pd.getString(CONFIG_DATABASE_KEY, json); + auto default_return = pd.GetString(CONFIG_DATABASE_KEY, json); ASSERT_EQ(default_return, json); } @@ -336,7 +336,7 @@ TEST(SocketserverConfigTest, GetBoolCallExpected) StubPersistentDictionary pd; const std::string key{}; const bool defaultBoolValue{}; - auto default_return = pd.getBool(key, defaultBoolValue); + auto default_return = pd.GetBool(key, defaultBoolValue); ASSERT_EQ(default_return, defaultBoolValue); } diff --git a/score/mw/log/BUILD b/score/mw/log/BUILD index a86b2fd..004b98a 100644 --- a/score/mw/log/BUILD +++ b/score/mw/log/BUILD @@ -13,8 +13,16 @@ load("@rules_cc//cc:defs.bzl", "cc_library") +COMPILER_WARNING_FEATURES = [ + "treat_warnings_as_errors", + "additional_warnings", + "strict_warnings", +] + cc_library( name = "log", + features = COMPILER_WARNING_FEATURES, + tags = ["FFI"], visibility = ["//visibility:public"], deps = [ "//score/mw/log/detail/common:recorder_factory", diff --git a/score/mw/log/detail/data_router/BUILD b/score/mw/log/detail/data_router/BUILD index 9ebad9a..1aad812 100644 --- a/score/mw/log/detail/data_router/BUILD +++ b/score/mw/log/detail/data_router/BUILD @@ -56,7 +56,10 @@ cc_library( "//score/mw/log/detail/common:dlt_content_formatting", "//score/mw/log/detail/common:statistics_reporter", "//score/mw/log/detail/data_router/shared_memory:writer", + "//score/mw/log/detail/utils/signal_handling", "//score/mw/log/legacy_non_verbose_api", + "@score_baselibs//score/concurrency", + "@score_baselibs//score/concurrency:thread_pool", "@score_baselibs//score/language/futurecpp", "@score_baselibs//score/memory/shared", # Preperation for full rollout of (Ticket-105741), build cyclic dependency is broken (runtime, in next commit) "@score_baselibs//score/mw/log:recorder", @@ -71,7 +74,7 @@ cc_library( "@score_baselibs//score/os:pthread", "@score_baselibs//score/os:stdlib", "@score_baselibs//score/os/utils:signal", - "@score_communication//score/mw/com/message_passing", + "@score_communication//score/message_passing", ], ) @@ -134,7 +137,7 @@ cc_test( "@score_baselibs//score/os/mocklib:stdlib_mock", "@score_baselibs//score/os/mocklib:unistd_mock", "@score_baselibs//score/os/utils/mocklib:signal_mock", - "@score_communication//score/mw/com/message_passing:mock", + "@score_communication//score/message_passing:mock", ], ) @@ -154,15 +157,16 @@ cc_library( ], hdrs = [ "data_router_messages.h", + "message_passing_config.h", ], features = COMPILER_WARNING_FEATURES, tags = ["FFI"], visibility = [ "//score/datarouter:__pkg__", + "//score/datarouter/test:__subpackages__", "//score/mw/log/detail/slog:__pkg__", ], deps = [ "@score_baselibs//score/mw/log/detail:logging_identifier", - "@score_communication//score/mw/com/message_passing", ], ) diff --git a/score/mw/log/detail/data_router/data_router_message_client_factory_test.cpp b/score/mw/log/detail/data_router/data_router_message_client_factory_test.cpp index 1830fcc..bcc634c 100644 --- a/score/mw/log/detail/data_router/data_router_message_client_factory_test.cpp +++ b/score/mw/log/detail/data_router/data_router_message_client_factory_test.cpp @@ -15,8 +15,6 @@ #include "score/os/mocklib/mock_pthread.h" #include "score/os/mocklib/unistdmock.h" #include "score/os/utils/mocklib/signalmock.h" -#include "score/mw/com/message_passing/receiver_mock.h" -#include "score/mw/com/message_passing/sender_mock.h" #include "score/mw/log/configuration/configuration.h" #include "score/mw/log/detail/data_router/data_router_message_client_factory_impl.h" #include "score/mw/log/detail/data_router/data_router_message_client_impl.h" diff --git a/score/mw/log/detail/data_router/data_router_message_client_impl.cpp b/score/mw/log/detail/data_router/data_router_message_client_impl.cpp index ab70fca..9692312 100644 --- a/score/mw/log/detail/data_router/data_router_message_client_impl.cpp +++ b/score/mw/log/detail/data_router/data_router_message_client_impl.cpp @@ -15,12 +15,13 @@ #include "score/assert.hpp" #include "score/os/utils/signal.h" -#include "score/mw/log/detail/data_router/data_router_messages.h" +#include "score/mw/log/detail/data_router/message_passing_config.h" #include "score/mw/log/detail/error.h" #include "score/mw/log/detail/initialization_reporter.h" +#include "score/os/utils/signal_impl.h" +#include "score/mw/log/detail/utils/signal_handling/signal_handling.h" #include -#include #include namespace score @@ -34,31 +35,6 @@ namespace detail using std::chrono_literals::operator"" ms; -namespace -{ - -constexpr std::size_t kNumberOfThread{1UL}; -constexpr std::size_t kMaxNumberMessagesInReceiverQueue{1UL}; -constexpr std::int32_t kSenderMaxNumberOfSendRetries{1000}; -constexpr std::chrono::milliseconds kSenderRetrySendDelay = std::chrono::milliseconds(100); -constexpr std::chrono::milliseconds kSenderRetryConnectDelay = std::chrono::milliseconds(100); -constexpr std::chrono::milliseconds kReceiverMessageLoopDelay = std::chrono::milliseconds(10); -constexpr std::size_t kStartIndexOfRandomFileName{13UL}; - -template -auto BuildMessageImpl(const DatarouterMessageIdentifier id, const Payload& payload, const pid_t pid) -> Message -{ - Message message; - message.payload = payload; - message.id = ToMessageId(id); - message.pid = pid; - return message; -} - -void SenderLoggerCallback(score::mw::com::message_passing::LogFunction) {} - -} // namespace - DatarouterMessageClientImpl::DatarouterMessageClientImpl(const MsgClientIdentifiers& ids, MsgClientBackend backend, MsgClientUtils utils, @@ -73,15 +49,13 @@ DatarouterMessageClientImpl::DatarouterMessageClientImpl(const MsgClientIdentifi shared_memory_writer_{backend.GetShMemWriter()}, writer_file_name_{backend.GetWriterFilename()}, message_passing_factory_{std::move(backend.GetMsgPassingFactory())}, - monotonic_resource_buffer_{}, - monotonic_resource_{monotonic_resource_buffer_.data(), - monotonic_resource_buffer_.size(), - score::cpp::pmr::null_memory_resource()}, stop_source_{stop_source}, - thread_pool_{kNumberOfThread, &monotonic_resource_}, + sender_state_change_mutex_{}, + state_condition_{}, + sender_state_{}, sender_{nullptr}, receiver_{nullptr}, - connect_task_{} + connect_thread_{} { } @@ -103,20 +77,47 @@ void DatarouterMessageClientImpl::Run() void DatarouterMessageClientImpl::RunConnectTask() { // Since waiting for Datarouter to connect is a blocking operation we have to do this asynchronously. - // clang-format off - connect_task_ = thread_pool_.Submit( - [this](const score::cpp::stop_token&) noexcept - { - ConnectToDatarouter(); - }); + connect_thread_ = score::cpp::jthread([this]() noexcept { + ConnectToDatarouter(); + }); } void DatarouterMessageClientImpl::ConnectToDatarouter() noexcept { BlockTermSignal(); - SetThreadName(); - CreateSender(); + + if (!CreateSender().has_value()) + { + ReportInitializationError(score::mw::log::detail::Error::kFailedToCreateMessagePassingClient, + "Failed to create Message Passing Client.", + msg_client_ids_.GetAppID().GetStringView()); + RequestInternalShutdown(); + return; + } + + // Wait for the sender to be in Ready state before starting receiver + { + std::unique_lock lock(sender_state_change_mutex_); + state_condition_.wait(lock, [this]() { + return (sender_state_.has_value() && + sender_state_.value() == score::message_passing::IClientConnection::State::kReady) || + stop_source_.stop_requested(); + }); + } + + if (stop_source_.stop_requested()) + { + RequestInternalShutdown(); + return; + } + + if (!sender_state_.has_value() || sender_state_.value() != score::message_passing::IClientConnection::State::kReady) + { + RequestInternalShutdown(); + return; + } + if (StartReceiver() == false) { RequestInternalShutdown(); @@ -177,31 +178,46 @@ void DatarouterMessageClientImpl::SetThreadName() noexcept void DatarouterMessageClientImpl::SetupReceiver() noexcept { - // Initialization of Array with client ids - // coverity[autosar_cpp14_m8_5_2_violation] - const std::array allowed_uids{msg_client_ids_.GetDatarouterUID()}; - const score::mw::com::message_passing::ReceiverConfig receiver_config{ - static_cast(kMaxNumberMessagesInReceiverQueue), kReceiverMessageLoopDelay}; - receiver_ = message_passing_factory_->CreateReceiver( - msg_client_ids_.GetReceiverID(), thread_pool_, allowed_uids, receiver_config, &monotonic_resource_); - - // clang-format off - receiver_->Register(ToMessageId(DatarouterMessageIdentifier::kAcquireRequest), - [this](std::uint64_t, pid_t) noexcept - { - this->OnAcquireRequest(); - }); + const score::message_passing::ServiceProtocolConfig service_protocol_config{ + msg_client_ids_.GetReceiverID(), MessagePassingConfig::kMaxMessageSize, 0U, 0U}; + + const score::message_passing::IServerFactory::ServerConfig server_config{ + MessagePassingConfig::kMaxReceiverQueueSize, 0U, 0U}; + receiver_ = message_passing_factory_->CreateServer(service_protocol_config, server_config); } bool DatarouterMessageClientImpl::StartReceiver() { // When the receiver starts listening, receive callbacks may be called that use the sender to reply. // Thus we must create the sender before starting to listen to messages. - // Note since the thread_pool_ shall only have one thread, the receiver callback may only be called after the - // connect task finished. + // Note that the receiver callback may only be called after the connect task finished. SCORE_LANGUAGE_FUTURECPP_PRECONDITION_PRD_MESSAGE(sender_ != nullptr, "The sender must be created before the receiver."); - const auto result = receiver_->StartListening(); + auto connect_callback = [this](score::message_passing::IServerConnection& connection) noexcept -> std::uintptr_t { + const auto result = SignalHandling::PThreadBlockSigTerm(this->utils_.GetSignal()); + const pid_t client_pid = connection.GetClientIdentity().pid; + return static_cast(client_pid); + }; + auto disconnect_callback = [this](score::message_passing::IServerConnection& /*connection*/) noexcept { + this->RequestInternalShutdown(); + }; + auto received_send_message_callback = [this]( + score::message_passing::IServerConnection& /*connection*/, + const score::cpp::span /*message*/) noexcept -> score::cpp::blank { + this->OnAcquireRequest(); + return {}; + }; + auto received_send_message_with_reply_callback = + [](score::message_passing::IServerConnection& /*connection*/, + score::cpp::span /*message*/) noexcept -> score::cpp::blank { + return {}; + }; + + const auto result = receiver_->StartListening(connect_callback, + disconnect_callback, + received_send_message_callback, + received_send_message_with_reply_callback); + if (result.has_value() == false) { const std::string underlying_error = result.error().ToString(); @@ -231,7 +247,6 @@ void DatarouterMessageClientImpl::RequestInternalShutdown() noexcept UnlinkSharedMemoryFile(); std::ignore = stop_source_.request_stop(); - thread_pool_.Shutdown(); } void DatarouterMessageClientImpl::CheckExitRequestAndSendConnectMessage() noexcept @@ -255,28 +270,18 @@ void DatarouterMessageClientImpl::SendConnectMessage() noexcept if (use_dynamic_datarouter_ids_ && (writer_file_name_.size() > - (kStartIndexOfRandomFileName + msg.GetRandomPart().size() + static_cast(1)))) + (MessagePassingConfig::kRandomFilenameStartIndex + msg.GetRandomPart().size() + static_cast(1)))) { auto start_iter = writer_file_name_.begin(); - std::advance(start_iter, static_cast(kStartIndexOfRandomFileName)); + std::advance(start_iter, static_cast(MessagePassingConfig::kRandomFilenameStartIndex)); auto random_part = msg.GetRandomPart(); std::ignore = std::copy_n(start_iter, random_part.size(), random_part.begin()); msg.SetRandomPart(random_part); } - score::mw::com::message_passing::MediumMessagePayload payload{}; - static_assert(sizeof(msg) <= payload.size(), "Connect message too large"); - static_assert(std::is_trivially_copyable::value, "Message must be copyable"); - // Suppress "AUTOSAR C++14 M5-2-8" rule. The rule declares: - // An object with integer type or pointer to void type shall not be converted to an object with pointer type. - // But we need to convert void pointer to bytes for serialization purposes, no out of bounds there - // coverity[autosar_cpp14_m5_2_8_violation] - const score::cpp::span message_span{static_cast(static_cast(&msg)), - sizeof(msg)}; - // coverity[autosar_cpp14_m5_0_16_violation:FALSE] - std::ignore = std::copy(message_span.begin(), message_span.end(), payload.begin()); - - SendMessage(BuildMessage(DatarouterMessageIdentifier::kConnect, payload)); + const auto message = SerializeMessage(DatarouterMessageIdentifier::kConnect, msg); + + SendMessage(message); } void DatarouterMessageClientImpl::Shutdown() noexcept @@ -286,35 +291,52 @@ void DatarouterMessageClientImpl::Shutdown() noexcept std::this_thread::sleep_for(std::chrono::milliseconds(100)); } std::ignore = stop_source_.request_stop(); - thread_pool_.Shutdown(); - if (connect_task_.Valid() == true) + // Notify waiting threads in case they are waiting for state change + state_condition_.notify_all(); + + // Request the jthread to stop and wait for it to finish + if (connect_thread_.joinable()) { - connect_task_.Abort(); - std::ignore = connect_task_.Wait(); + connect_thread_.request_stop(); + connect_thread_.join(); } + receiver_.reset(); - sender_.reset(); + { + std::unique_lock lock(sender_mutex_); + sender_.reset(); + } // Block until all pending tasks and threads have finished. UnlinkSharedMemoryFile(); } -void DatarouterMessageClientImpl::CreateSender() noexcept +score::cpp::expected_blank DatarouterMessageClientImpl::CreateSender() noexcept { - // We are not using the stop token from the current task, because the sender shall be used even after the - // current task exited. The connect task thus can only be stopped if the stop_source_ is triggered. - const score::mw::com::message_passing::SenderConfig sender_config{ - kSenderMaxNumberOfSendRetries, kSenderRetrySendDelay, kSenderRetryConnectDelay}; - constexpr std::string_view kDatarouterReceiverIdentifier{"/logging.datarouter_recv"}; - sender_ = message_passing_factory_->CreateSender(kDatarouterReceiverIdentifier, - stop_source_.get_token(), - sender_config, - &SenderLoggerCallback, - &monotonic_resource_); - - // The creation of the sender is blocking until Datarouter is available. - // Thus at this point, either Datarouter was available, or the stop_source_ was triggered. - // The Sender interface currently does not expose the information if the Sender could be connected successfully to a - // receiver. + const score::message_passing::ServiceProtocolConfig& protocol_config{ + MessagePassingConfig::kDatarouterReceiverIdentifier, + MessagePassingConfig::kMaxMessageSize, + MessagePassingConfig::kMaxReplySize, + MessagePassingConfig::kMaxNotifySize}; + const score::message_passing::IClientFactory::ClientConfig& client_config{0, 10, false, true, false}; + sender_ = message_passing_factory_->CreateClient(protocol_config, client_config); + + if (sender_ == nullptr) + { + std::cerr << "[[mw::log]] Application (PID: " << msg_client_ids_.GetThisProcID() + << ") failed to create Message Passing Client." << '\n'; + return score::cpp::make_unexpected(score::os::Error::createFromErrno(ENOMEM)); + } + + auto state_callback = [this](score::message_passing::IClientConnection::State state) noexcept { + { + std::lock_guard callback_lock(sender_state_change_mutex_); + sender_state_ = state; + } + state_condition_.notify_all(); + }; + + sender_->Start(state_callback, score::message_passing::IClientConnection::NotifyCallback{}); + return {}; } void DatarouterMessageClientImpl::OnAcquireRequest() noexcept @@ -324,19 +346,9 @@ void DatarouterMessageClientImpl::OnAcquireRequest() noexcept // Acquire data and prepare the response. const auto acquire_result = shared_memory_writer_.ReadAcquire(); - // TODO: Use ShortMessagePayload instead: - score::mw::com::message_passing::MediumMessagePayload payload{}; - static_assert(sizeof(payload) >= sizeof(acquire_result), "payload shall be large enough"); - const score::cpp::span acquire_result_span{ - // Suppress "AUTOSAR C++14 M5-2-8" rule. The rule declares: - // An object with integer type or pointer to void type shall not be converted to an object with pointer type. - // But we need to convert void pointer to bytes for serialization purposes, no out of bounds there - // coverity[autosar_cpp14_m5_2_8_violation] - static_cast(static_cast(&acquire_result)), - sizeof(acquire_result)}; - // coverity[autosar_cpp14_m5_0_16_violation:FALSE] - std::ignore = std::copy(acquire_result_span.begin(), acquire_result_span.end(), payload.begin()); - SendMessage(BuildMessage(DatarouterMessageIdentifier::kAcquireResponse, payload)); + const auto message = SerializeMessage(DatarouterMessageIdentifier::kAcquireResponse, acquire_result); + + SendMessage(message); } void DatarouterMessageClientImpl::HandleFirstMessageReceived() noexcept @@ -350,15 +362,22 @@ void DatarouterMessageClientImpl::HandleFirstMessageReceived() noexcept UnlinkSharedMemoryFile(); } -template -void DatarouterMessageClientImpl::SendMessage(const Message& message) noexcept +void DatarouterMessageClientImpl::SendMessage(const score::cpp::span& message) noexcept { - auto result = sender_->Send(message); + score::cpp::expected_blank result; + { + std::unique_lock lock(sender_mutex_); + if (sender_ != nullptr) + { + result = sender_->Send(message); + } + } if (result.has_value() == false) { // The sender will retry sending the message for 10 s (retry_delay * number_of_retries). // If sending the message does not succeed despite all retries we assume Datarouter has crashed or is hanging // and consequently shutdown the logging in the client. + // Send() already checks if the sender is in kReady state and returns EINVAL if not. const auto error_details = result.error().ToStringContainer(result.error()); ReportInitializationError(score::mw::log::detail::Error::kFailedToSendMessageToDatarouter, @@ -369,15 +388,6 @@ void DatarouterMessageClientImpl::SendMessage(const Message& message) noexcept } } -score::mw::com::message_passing::MediumMessage DatarouterMessageClientImpl::BuildMessage( - const DatarouterMessageIdentifier& id, - const score::mw::com::message_passing::MediumMessagePayload& payload) const noexcept -{ - return BuildMessageImpl( - id, payload, msg_client_ids_.GetThisProcID()); -} - void DatarouterMessageClientImpl::UnlinkSharedMemoryFile() noexcept { if (unlinked_shared_memory_file_) diff --git a/score/mw/log/detail/data_router/data_router_message_client_impl.h b/score/mw/log/detail/data_router/data_router_message_client_impl.h index 4dabb37..6d8ba66 100644 --- a/score/mw/log/detail/data_router/data_router_message_client_impl.h +++ b/score/mw/log/detail/data_router/data_router_message_client_impl.h @@ -14,16 +14,21 @@ #ifndef SCORE_MW_LOG_DETAIL_DATA_ROUTER_MESSAGE_CLIENT_IMPL_H #define SCORE_MW_LOG_DETAIL_DATA_ROUTER_MESSAGE_CLIENT_IMPL_H +#include "score/expected.hpp" +#include "score/jthread.hpp" #include "score/optional.hpp" -#include "score/concurrency/thread_pool.h" -#include "score/mw/com/message_passing/i_receiver.h" -#include "score/mw/com/message_passing/i_sender.h" +#include "score/stop_token.hpp" +#include "score/message_passing/i_server_connection.h" +#include "score/os/errno.h" #include "score/mw/log/detail/data_router/data_router_message_client.h" #include "score/mw/log/detail/data_router/data_router_message_client_backend.h" #include "score/mw/log/detail/data_router/data_router_message_client_identifiers.h" #include "score/mw/log/detail/data_router/data_router_message_client_utils.h" #include "score/mw/log/detail/data_router/data_router_messages.h" +#include +#include + namespace score { namespace mw @@ -44,10 +49,8 @@ constexpr std::size_t GetMonotonicResourceSize() noexcept /// Remarks on thread safety: /// The methods of the parent interface are not thread safe. /// The Shutdown() method must only be called after the Run() method. -/// After calling Run() there shall be a single thread in the thread pool -/// that delivers callbacks to the On*Request() methods. Since the thread -/// pool only contains a single thread no additional internal synchronization -/// mutex is necessary. +/// After calling Run() there shall be a single background thread +/// that delivers callbacks to the On*Request() methods. class DatarouterMessageClientImpl : public DatarouterMessageClient { public: @@ -68,7 +71,10 @@ class DatarouterMessageClientImpl : public DatarouterMessageClient void Shutdown() noexcept override; void SetupReceiver() noexcept; - void CreateSender() noexcept; + + /// \brief Creates the message passing client (sender) for communication with Datarouter. + /// \returns An empty expected on success, or an error if the sender could not be created. + score::cpp::expected_blank CreateSender() noexcept; /// \pre SetupReceiver and CreateSender() called before. /// \returns true if the receiver was started successfully. @@ -98,12 +104,7 @@ class DatarouterMessageClientImpl : public DatarouterMessageClient void RequestInternalShutdown() noexcept; void CheckExitRequestAndSendConnectMessage() noexcept; - score::mw::com::message_passing::MediumMessage BuildMessage( - const DatarouterMessageIdentifier& id, - const score::mw::com::message_passing::MediumMessagePayload& payload) const noexcept; - - template - void SendMessage(const Message& message) noexcept; + void SendMessage(const score::cpp::span& message) noexcept; bool run_started_; @@ -120,22 +121,23 @@ class DatarouterMessageClientImpl : public DatarouterMessageClient std::string writer_file_name_; std::unique_ptr message_passing_factory_; - std::array monotonic_resource_buffer_; - score::cpp::pmr::monotonic_buffer_resource monotonic_resource_; score::cpp::stop_source stop_source_; - score::concurrency::ThreadPool thread_pool_; + mutable std::mutex sender_state_change_mutex_; + mutable std::mutex sender_mutex_; + std::condition_variable state_condition_; + score::cpp::optional sender_state_; // The construction/destruction order is critical here! // Sender and receiver both may contain running tasks. // Receiver tasks (callbacks) may use the sender. - // Thus the receiver needs to destruct first, and then the receiver. - // Finally it is safe to destroy the thread pool. + // Thus the receiver needs to destruct first, and then the sender. + // Finally it is safe to join the connect thread. // Only then we can ensure that there are no concurrent tasks // accessing private data from another thread. - score::cpp::pmr::unique_ptr sender_; - score::cpp::pmr::unique_ptr receiver_; - score::concurrency::TaskResult connect_task_; + score::cpp::pmr::unique_ptr sender_; + score::cpp::pmr::unique_ptr receiver_; + score::cpp::jthread connect_thread_; }; } // namespace detail diff --git a/score/mw/log/detail/data_router/data_router_message_client_test.cpp b/score/mw/log/detail/data_router/data_router_message_client_test.cpp index 9c6df03..250830e 100644 --- a/score/mw/log/detail/data_router/data_router_message_client_test.cpp +++ b/score/mw/log/detail/data_router/data_router_message_client_test.cpp @@ -12,17 +12,20 @@ ********************************************************************************/ #include "score/assert_support.hpp" +#include "score/message_passing/mock/client_connection_mock.h" +#include "score/message_passing/mock/server_connection_mock.h" +#include "score/message_passing/mock/server_mock.h" +#include "score/message_passing/server_types.h" #include "score/os/mocklib/mock_pthread.h" #include "score/os/mocklib/unistdmock.h" #include "score/os/utils/mocklib/signalmock.h" -#include "score/mw/com/message_passing/receiver_mock.h" -#include "score/mw/com/message_passing/sender_mock.h" #include "score/mw/log/detail/data_router/data_router_message_client_impl.h" #include "score/mw/log/detail/data_router/message_passing_factory_mock.h" #include "gmock/gmock.h" #include "gtest/gtest.h" - +#include +#include namespace score { namespace mw @@ -43,9 +46,39 @@ using ::testing::Return; using ::testing::ReturnRef; using ::testing::StrEq; +MATCHER_P(CompareServiceProtocol, expected, "") +{ + if (arg.identifier != expected.identifier || arg.max_send_size != expected.max_send_size || + arg.max_reply_size != expected.max_reply_size || arg.max_notify_size != expected.max_notify_size) + { + + return false; + } + return true; +} + +MATCHER_P(CompareServerConfig, expected, "") +{ + if (arg.max_queued_sends != expected.max_queued_sends || + arg.pre_alloc_connections != expected.pre_alloc_connections || + arg.max_queued_notifies != expected.max_queued_notifies) + { + return false; + } + return true; +} + +MATCHER_P(CompareClientConfig, expected, "") +{ + if (arg.max_async_replies != expected.max_async_replies || arg.max_queued_sends != expected.max_queued_sends || + arg.fully_ordered != expected.fully_ordered || arg.truly_async != expected.truly_async) + { + return false; + } + return true; +} + const std::string_view kDatarouterReceiverIdentifier{"/logging.datarouter_recv"}; -const auto kAcquireRequestPayload = 0u; // Arbitrary value since payload is ignored. -const auto kDatarouterPid = 324u; const std::string kClientReceiverIdentifier = "/logging.app.1234"; const auto kMwsrFileName = "/tmp" + kClientReceiverIdentifier + ".shmem"; const auto kAppid = LoggingIdentifier{"TeAp"}; @@ -55,6 +88,8 @@ const pid_t kThisProcessPid = 99u; constexpr pthread_t kThreadId = 42; constexpr auto kLoggerThreadName = "logger"; constexpr uid_t datarouter_dummy_uid = 111; +constexpr std::uint32_t kMaxSendBytes{17U}; +constexpr std::uint32_t kMaxNumberMessagesInReceiverQueue{0UL}; class DatarouterMessageClientFixture : public ::testing::Test { @@ -74,7 +109,7 @@ class DatarouterMessageClientFixture : public ::testing::Test unistd_mock_ = unistd_mock.get(); auto pthread_mock = score::cpp::pmr::make_unique>(memory_resource); pthread_mock_ = pthread_mock.get(); - auto signal_mock = score::cpp::pmr::make_unique>(memory_resource); + auto signal_mock = score::cpp::pmr::make_unique(memory_resource); signal_mock_ = signal_mock.get(); auto message_passing_factory = std::make_unique>(); message_passing_factory_ = message_passing_factory.get(); @@ -118,78 +153,120 @@ class DatarouterMessageClientFixture : public ::testing::Test .WillOnce(Return(score::cpp::make_unexpected(score::os::Error::createUnspecifiedError()))); } - score::mw::com::message_passing::ReceiverMock* ExpectReceiverCreated() + score::message_passing::ServerMock* ExpectReceiverCreated() { - auto receiver = score::cpp::pmr::make_unique>( + auto receiver = score::cpp::pmr::make_unique>( score::cpp::pmr::get_default_resource()); auto receiver_ptr = receiver.get(); + const score::message_passing::ServiceProtocolConfig service_protocol_config{ + kClientReceiverIdentifier, kMaxSendBytes, 0U, 0U}; + + const score::message_passing::IServerFactory::ServerConfig server_config{ + kMaxNumberMessagesInReceiverQueue, 0U, 0U}; EXPECT_CALL(*message_passing_factory_, - CreateReceiver(Eq(std::string_view{kClientReceiverIdentifier}), _, _, _, _)) + CreateServer(CompareServiceProtocol(service_protocol_config), CompareServerConfig(server_config))) .WillOnce(Return(ByMove(std::move(receiver)))); return receiver_ptr; } - void ExpectReceiverRegisterCallbacks( - score::mw::com::message_passing::ReceiverMock* receiver_ptr, - score::mw::com::message_passing::IReceiver::ShortMessageReceivedCallback* acquire_callback = nullptr) + void ExpectReceiverStartListening( + score::message_passing::ServerMock* receiver_ptr, + score::message_passing::ConnectCallback* connect_callback = nullptr, + score::message_passing::DisconnectCallback* disconnect_callback = nullptr, + score::message_passing::MessageCallback* sent_callback = nullptr, + score::message_passing::MessageCallback* sent_with_reply_callback = nullptr, + score::cpp::expected_blank result = score::cpp::expected_blank{}) { EXPECT_CALL(*receiver_ptr, - Register(static_cast(DatarouterMessageIdentifier::kAcquireRequest), - Matcher(_))) - .WillOnce( - [acquire_callback](std::int8_t, - score::mw::com::message_passing::IReceiver::ShortMessageReceivedCallback callback) { - if (acquire_callback != nullptr) - { - *acquire_callback = std::move(callback); - } - }); + StartListening(Matcher(_), + Matcher(_), + Matcher(_), + Matcher(_))) + .WillOnce([connect_callback, disconnect_callback, sent_callback, sent_with_reply_callback, result]( + score::message_passing::ConnectCallback con_callback, + score::message_passing::DisconnectCallback discon_callback, + score::message_passing::MessageCallback sn_callback, + score::message_passing::MessageCallback sn_rep_callback) { + if (connect_callback != nullptr) + { + *connect_callback = std::move(con_callback); + } + if (disconnect_callback != nullptr) + { + *disconnect_callback = std::move(discon_callback); + } + if (sent_callback != nullptr) + { + *sent_callback = std::move(sn_callback); + } + if (sent_with_reply_callback != nullptr) + { + *sent_with_reply_callback = std::move(sn_rep_callback); + } + return result; + }); } - void ExpectReceiverStartListening( - score::mw::com::message_passing::ReceiverMock* receiver_ptr, - score::cpp::expected_blank result = score::cpp::expected_blank{}) + score::message_passing::ClientConnectionMock* ExpectSenderCreation( + score::message_passing::IClientConnection::StateCallback* state_callback = nullptr, + std::promise* callback_registered = nullptr) { - EXPECT_CALL(*receiver_ptr, StartListening()).WillOnce(Return(result)); + sender_mock_in_transit_ = + score::cpp::pmr::make_unique>( + score::cpp::pmr::get_default_resource()); + auto sender_mock = sender_mock_in_transit_.get(); + const score::message_passing::ServiceProtocolConfig service_protocol_config{ + kDatarouterReceiverIdentifier, kMaxSendBytes, 0U, 0U}; + + const score::message_passing::IClientFactory::ClientConfig& client_config{0, 10, false, true, false}; + + EXPECT_CALL(*message_passing_factory_, + CreateClient(CompareServiceProtocol(service_protocol_config), CompareClientConfig(client_config))) + .WillOnce(Return(ByMove(std::move(sender_mock_in_transit_)))); + + EXPECT_CALL(*sender_mock, + Start(Matcher(_), + Matcher(_))) + .WillOnce([state_callback, callback_registered]( + score::message_passing::IClientConnection::StateCallback st_callback, + score::message_passing::IClientConnection::NotifyCallback) { + if (state_callback != nullptr) + { + *state_callback = std::move(st_callback); + } + if (callback_registered != nullptr) + { + callback_registered->set_value(); + } + }); + + return sender_mock; } - score::mw::com::message_passing::SenderMock* ExpectSenderCreation() + void ExpectClientDestruction(score::message_passing::ClientConnectionMock* sender_mock) { - // We need to implicitly transfer the ownership to the callback via the class member. - // Necessary workaround because mutable&move capture callbacks are not accepted by gmock. - // Since the callback should be called exactly once there are no memory errors. - // If the code violates the expectation, the sanitizers and valgrind will detect memory errors. - sender_mock_in_transit_ = score::cpp::pmr::make_unique>( - score::cpp::pmr::get_default_resource()); - auto callback = [this](const std::string_view, - const score::cpp::stop_token&, - const score::mw::com::message_passing::SenderConfig&, - score::mw::com::message_passing::LoggingCallback log_callback, - score::cpp::pmr::memory_resource* /*resource*/) - -> score::cpp::pmr::unique_ptr { - log_callback([](std::ostream&) { - FAIL() << "expected to drop the logs"; - }); - return std::move(sender_mock_in_transit_); - }; - EXPECT_CALL(*message_passing_factory_, CreateSender(Eq(kDatarouterReceiverIdentifier), _, _, _, _)) - .WillOnce(callback); - return sender_mock_in_transit_.get(); + EXPECT_CALL(*sender_mock, Destruct()); } - void ExpectSendAcquireResponse(score::mw::com::message_passing::SenderMock* sender_ptr, + void ExpectServerDestruction(score::message_passing::ServerMock* receiver_mock) + { + EXPECT_CALL(*receiver_mock, Destruct()); + } + void ExpectSendAcquireResponse(score::message_passing::ClientConnectionMock* sender_ptr, ReadAcquireResult expected_content, score::cpp::expected_blank result = score::cpp::expected_blank{}) { - EXPECT_CALL(*sender_ptr, Send(Matcher(_))) - .WillOnce([expected_content, result](const score::mw::com::message_passing::MediumMessage& msg) - -> score::cpp::expected_blank { - EXPECT_EQ(msg.id, ToMessageId(DatarouterMessageIdentifier::kAcquireResponse)); - ReadAcquireResult data; - memcpy(&data, msg.payload.data(), sizeof(data)); - EXPECT_EQ(data.acquired_buffer, expected_content.acquired_buffer); - return result; - }); + EXPECT_CALL(*sender_ptr, Send(Matcher>(_))) + .WillOnce( + [expected_content, result](score::cpp::span msg) -> score::cpp::expected_blank { + EXPECT_EQ(msg.front(), score::cpp::to_underlying(DatarouterMessageIdentifier::kAcquireResponse)); + ReadAcquireResult data; + const auto payload = msg.subspan(1); + + memcpy(&data, payload.data(), sizeof(data)); + EXPECT_EQ(data.acquired_buffer, expected_content.acquired_buffer); + return result; + }); } void ExpectUnlinkMwsrWriterFile(bool unlink_successful = true) @@ -225,11 +302,10 @@ class DatarouterMessageClientFixture : public ::testing::Test }); } - void SendAcquireRequestAndExpectResponse( - score::mw::com::message_passing::IReceiver::ShortMessageReceivedCallback& acquire_callback, - score::mw::com::message_passing::SenderMock** sender_ptr, - bool first_message, - bool unlink_successful = true) + void SendAcquireRequestAndExpectResponse(score::message_passing::MessageCallback& acquire_callback, + score::message_passing::ClientConnectionMock** sender_ptr, + bool first_message, + bool unlink_successful = true) { ReadAcquireResult acquired_data{}; acquired_data.acquired_buffer = shared_data_.control_block.switch_count_points_active_for_writing.load(); @@ -242,22 +318,32 @@ class DatarouterMessageClientFixture : public ::testing::Test ExpectSendAcquireResponse(*sender_ptr, acquired_data); - acquire_callback(kAcquireRequestPayload, kDatarouterPid); + score::message_passing::ServerConnectionMock connection; + const score::cpp::span message{}; + acquire_callback(connection, message); } void ExpectSenderAndReceiverCreation( - score::mw::com::message_passing::ReceiverMock** receiver_ptr = nullptr, - score::mw::com::message_passing::SenderMock** sender_ptr = nullptr, + score::message_passing::ServerMock** receiver_ptr, + score::message_passing::ClientConnectionMock** sender_ptr, + + score::message_passing::IClientConnection::StateCallback* state_callback = nullptr, + std::promise* callback_registered = nullptr, score::cpp::expected_blank listen_result = score::cpp::expected_blank{}, - score::mw::com::message_passing::IReceiver::ShortMessageReceivedCallback* acquire_callback = nullptr, - bool blockTerminationSignalPass = true) + score::message_passing::ConnectCallback* connect_callback = nullptr, + score::message_passing::DisconnectCallback* disconnect_callback = nullptr, + score::message_passing::MessageCallback* sent_callback = nullptr, + score::message_passing::MessageCallback* sent_with_reply_callback = nullptr, + bool blockTerminationSignalPass = true, + bool receiver_start_listening = true) + { + std::ignore = blockTerminationSignalPass; // TODO: remove this param auto receiver = ExpectReceiverCreated(); if (receiver_ptr != nullptr) { *receiver_ptr = receiver; } - ExpectReceiverRegisterCallbacks(receiver, acquire_callback); if (blockTerminationSignalPass) { @@ -270,37 +356,44 @@ class DatarouterMessageClientFixture : public ::testing::Test ExpectSetLoggerThreadName(); - auto sender = ExpectSenderCreation(); + auto sender = ExpectSenderCreation(state_callback, callback_registered); if (sender_ptr != nullptr) { *sender_ptr = sender; } - ExpectReceiverStartListening(receiver, listen_result); + if (receiver_start_listening) + { + ExpectReceiverStartListening(receiver, + connect_callback, + disconnect_callback, + sent_callback, + sent_with_reply_callback, + listen_result); + } } - void ExpectSendConnectMessage(score::mw::com::message_passing::SenderMock* sender_ptr) + void ExpectSendConnectMessage(score::message_passing::ClientConnectionMock* sender_ptr) { - EXPECT_CALL(*sender_ptr, Send(An())) - .WillOnce( - [this](const score::mw::com::message_passing::MediumMessage& msg) -> score::cpp::expected_blank { - EXPECT_EQ(msg.pid, kThisProcessPid); - EXPECT_EQ(msg.id, ToMessageId(DatarouterMessageIdentifier::kConnect)); - std::array random_part; - if (dynamicDataRouterIdentifiers_ && !mwsrFileName_.empty()) - { - memcpy(random_part.data(), &mwsrFileName_.data()[13], random_part.size()); - } - else - { - random_part = {}; - } - - ConnectMessageFromClient expected_msg(kAppid, kUid, dynamicDataRouterIdentifiers_, random_part); - ConnectMessageFromClient received_msg; - memcpy(&received_msg, msg.payload.data(), sizeof(ConnectMessageFromClient)); - EXPECT_EQ(expected_msg, received_msg); - return {}; - }); + EXPECT_CALL(*sender_ptr, Send(Matcher>(_))) + .WillOnce([this](score::cpp::span msg) -> score::cpp::expected_blank { + EXPECT_EQ(msg.front(), score::cpp::to_underlying(DatarouterMessageIdentifier::kConnect)); + std::array random_part; + if (dynamicDataRouterIdentifiers_ && !mwsrFileName_.empty()) + { + memcpy(random_part.data(), &mwsrFileName_.data()[13], random_part.size()); + } + else + { + random_part = {}; + } + + ConnectMessageFromClient expected_msg(kAppid, kUid, dynamicDataRouterIdentifiers_, random_part); + ConnectMessageFromClient received_msg; + const auto payload = msg.subspan(1); + memcpy(&received_msg, payload.data(), sizeof(ConnectMessageFromClient)); + EXPECT_EQ(expected_msg, received_msg); + return {}; + }); } void ExpectSetLoggerThreadName(bool success = true) @@ -314,12 +407,15 @@ class DatarouterMessageClientFixture : public ::testing::Test EXPECT_CALL(*pthread_mock_, setname_np(kThreadId, StrEq(kLoggerThreadName))).WillOnce(Return(setname_result)); } - void ExecuteCreateSenderAndReceiverSequence(bool expect_receiver_success = true) + void ExecuteCreateSenderAndReceiverSequence( + bool expect_receiver_success = true, + score::message_passing::IClientConnection::StateCallback* state_callback = nullptr) { client_->SetupReceiver(); client_->BlockTermSignal(); client_->SetThreadName(); client_->CreateSender(); + (*state_callback)(score::message_passing::IClientConnection::State::kReady); EXPECT_EQ(client_->StartReceiver(), expect_receiver_success); } @@ -327,11 +423,12 @@ class DatarouterMessageClientFixture : public ::testing::Test bool dynamicDataRouterIdentifiers_{kDynamicDataRouterIdentifiers}; std::string mwsrFileName_{kMwsrFileName}; - score::cpp::pmr::unique_ptr> sender_mock_in_transit_; + score::cpp::pmr::unique_ptr> sender_mock_in_transit_; testing::StrictMock* unistd_mock_; testing::StrictMock* pthread_mock_; - testing::StrictMock* signal_mock_; + // TODO: bring back testing signal calls: + score::os::SignalMock* signal_mock_; SharedData shared_data_{}; SharedMemoryWriter shared_memory_writer_{InitializeSharedData(shared_data_), []() noexcept {}}; @@ -357,20 +454,6 @@ class MwsrFileNameEmptyFixture : public DatarouterMessageClientFixture MwsrFileNameEmptyFixture() : DatarouterMessageClientFixture(std::string()) {} }; -TEST_F(DatarouterMessageClientFixture, SetupReceiverShouldRegisterExpectedCallbacks) -{ - RecordProperty("ASIL", "B"); - RecordProperty("Description", - "Verifies that creating a distinct receiver will lead to registering of the proper callbacks."); - RecordProperty("TestType", "Interface test"); - RecordProperty("DerivationTechnique", "Generation and analysis of equivalence classes"); - - testing::InSequence order_matters; - auto receiver = ExpectReceiverCreated(); - ExpectReceiverRegisterCallbacks(receiver); - client_->SetupReceiver(); -} - TEST_F(DatarouterMessageClientFixture, CreateSenderShouldCreateSenderWithExpectedValues) { RecordProperty("ASIL", "B"); @@ -379,7 +462,8 @@ TEST_F(DatarouterMessageClientFixture, CreateSenderShouldCreateSenderWithExpecte RecordProperty("DerivationTechnique", "Generation and analysis of equivalence classes"); testing::InSequence order_matters; - ExpectSenderCreation(); + auto sender = ExpectSenderCreation(); + ExpectClientDestruction(sender); client_->CreateSender(); } @@ -392,8 +476,15 @@ TEST_F(DatarouterMessageClientFixture, StartReceiverShouldStartListenSuccessfull testing::InSequence order_matters; - ExpectSenderAndReceiverCreation(); - ExecuteCreateSenderAndReceiverSequence(); + score::message_passing::ClientConnectionMock* sender_ptr{}; + score::message_passing::ServerMock* receiver_ptr{}; + score::message_passing::IClientConnection::StateCallback state_callback; + + ExpectSenderAndReceiverCreation(&receiver_ptr, &sender_ptr, &state_callback); + + ExpectServerDestruction(receiver_ptr); + ExpectClientDestruction(sender_ptr); + ExecuteCreateSenderAndReceiverSequence(true, &state_callback); } TEST_F(DatarouterMessageClientFixture, StartReceiverWithoutSenderAndReceiverShouldFail) @@ -407,7 +498,6 @@ TEST_F(DatarouterMessageClientFixture, StartReceiverWithoutSenderAndReceiverShou EXPECT_DEATH(client_->StartReceiver(), ""); } - TEST_F(DatarouterMessageClientFixture, ReceiverStartListeningFailsShouldBeHandledGracefully) { RecordProperty("ASIL", "B"); @@ -417,10 +507,16 @@ TEST_F(DatarouterMessageClientFixture, ReceiverStartListeningFailsShouldBeHandle testing::InSequence order_matters; + score::message_passing::ClientConnectionMock* sender_ptr{}; + score::message_passing::ServerMock* receiver_ptr{}; + score::message_passing::IClientConnection::StateCallback state_callback; + auto start_listening_error = score::cpp::make_unexpected(score::os::Error::createFromErrno()); - ExpectSenderAndReceiverCreation(nullptr, nullptr, start_listening_error); + ExpectSenderAndReceiverCreation(&receiver_ptr, &sender_ptr, &state_callback, nullptr, start_listening_error); - ExecuteCreateSenderAndReceiverSequence(false); + ExpectServerDestruction(receiver_ptr); + ExpectClientDestruction(sender_ptr); + ExecuteCreateSenderAndReceiverSequence(false, &state_callback); } TEST_F(DatarouterMessageClientFixture, SendConnectMessageShouldSendExpectedPayload) @@ -430,12 +526,15 @@ TEST_F(DatarouterMessageClientFixture, SendConnectMessageShouldSendExpectedPaylo RecordProperty("TestType", "Interface test"); RecordProperty("DerivationTechnique", "Generation and analysis of equivalence classes"); + score::message_passing::IClientConnection::StateCallback state_callback; testing::InSequence order_matters; - auto sender = ExpectSenderCreation(); + auto sender = ExpectSenderCreation(&state_callback); ExpectSendConnectMessage(sender); + ExpectClientDestruction(sender); client_->CreateSender(); + state_callback(score::message_passing::IClientConnection::State::kReady); client_->SendConnectMessage(); } @@ -447,12 +546,15 @@ TEST_F(DynamicDataRouterIdentifiersFalseFixture, RecordProperty("TestType", "Interface test"); RecordProperty("DerivationTechnique", "Generation and analysis of equivalence classes"); + score::message_passing::IClientConnection::StateCallback state_callback; testing::InSequence order_matters; - auto sender = ExpectSenderCreation(); + auto sender = ExpectSenderCreation(&state_callback); ExpectSendConnectMessage(sender); + ExpectClientDestruction(sender); client_->CreateSender(); + state_callback(score::message_passing::IClientConnection::State::kReady); client_->SendConnectMessage(); } @@ -463,12 +565,15 @@ TEST_F(MwsrFileNameEmptyFixture, SendConnectMessageMwsrFileNameEmptyShouldSendEx RecordProperty("TestType", "Interface test"); RecordProperty("DerivationTechnique", "Generation and analysis of equivalence classes"); + score::message_passing::IClientConnection::StateCallback state_callback; testing::InSequence order_matters; - auto sender = ExpectSenderCreation(); + auto sender = ExpectSenderCreation(&state_callback); ExpectSendConnectMessage(sender); + ExpectClientDestruction(sender); client_->CreateSender(); + state_callback(score::message_passing::IClientConnection::State::kReady); client_->SendConnectMessage(); } @@ -478,16 +583,29 @@ TEST_F(DatarouterMessageClientFixture, ConnectToDatarouterShouldSendConnectMessa RecordProperty("Description", "Verifies the ability of sending connect message when connecting to datarouter."); RecordProperty("TestType", "Interface test"); RecordProperty("DerivationTechnique", "Generation and analysis of equivalence classes"); + score::message_passing::IClientConnection::StateCallback state_callback; + std::promise callback_registered; testing::InSequence order_matters; - score::mw::com::message_passing::SenderMock* sender_ptr{}; + score::message_passing::ClientConnectionMock* sender_ptr{}; + score::message_passing::ServerMock* receiver_ptr{}; - ExpectSenderAndReceiverCreation(nullptr, &sender_ptr); + ExpectSenderAndReceiverCreation(&receiver_ptr, &sender_ptr, &state_callback, &callback_registered); ExpectSendConnectMessage(sender_ptr); + ExpectServerDestruction(receiver_ptr); + ExpectClientDestruction(sender_ptr); client_->SetupReceiver(); - client_->ConnectToDatarouter(); + // We need to unblock waiting for the connection so we change the state in a separate thread + std::thread connect_thread([this]() noexcept { + client_->ConnectToDatarouter(); + }); + callback_registered.get_future().wait(); + + state_callback(score::message_passing::IClientConnection::State::kReady); + + connect_thread.join(); } TEST_F(DatarouterMessageClientFixture, ConnectToDatarouterGivenThatReceiverFailedShouldNotSendConnectMessage) @@ -499,12 +617,31 @@ TEST_F(DatarouterMessageClientFixture, ConnectToDatarouterGivenThatReceiverFaile testing::InSequence order_matters; - ExpectSenderAndReceiverCreation( - nullptr, nullptr, score::cpp::make_unexpected(score::os::Error::createFromErrno())); + score::message_passing::ClientConnectionMock* sender_ptr{}; + score::message_passing::ServerMock* receiver_ptr{}; + score::message_passing::IClientConnection::StateCallback state_callback; + std::promise callback_registered; + + ExpectSenderAndReceiverCreation(&receiver_ptr, + &sender_ptr, + &state_callback, + &callback_registered, + score::cpp::make_unexpected(score::os::Error::createFromErrno())); + ExpectUnlinkMwsrWriterFile(); + ExpectServerDestruction(receiver_ptr); + ExpectClientDestruction(sender_ptr); client_->SetupReceiver(); - client_->ConnectToDatarouter(); + // We need to unblock waiting for the connection so we change the state in a separate thread + std::thread connect_thread([this]() noexcept { + client_->ConnectToDatarouter(); + }); + + callback_registered.get_future().wait(); + state_callback(score::message_passing::IClientConnection::State::kReady); + + connect_thread.join(); } TEST_F(DatarouterMessageClientFixture, AcquireRequestShouldSendExpectedAcquireResponse) @@ -516,15 +653,30 @@ TEST_F(DatarouterMessageClientFixture, AcquireRequestShouldSendExpectedAcquireRe testing::InSequence order_matters; - score::mw::com::message_passing::SenderMock* sender_ptr{}; - score::mw::com::message_passing::ReceiverMock* receiver_ptr{}; - score::mw::com::message_passing::IReceiver::ShortMessageReceivedCallback acquire_callback; - ExpectSenderAndReceiverCreation(&receiver_ptr, &sender_ptr, {}, &acquire_callback); - - ExecuteCreateSenderAndReceiverSequence(); + score::message_passing::ClientConnectionMock* sender_ptr{}; + score::message_passing::ServerMock* receiver_ptr{}; + score::message_passing::ConnectCallback connect_callback; + score::message_passing::DisconnectCallback disconnect_callback; + score::message_passing::MessageCallback sent_callback; + score::message_passing::MessageCallback sent_with_reply_callback; + score::message_passing::IClientConnection::StateCallback state_callback; + + ExpectSenderAndReceiverCreation(&receiver_ptr, + &sender_ptr, + &state_callback, + nullptr, + {}, + &connect_callback, + &disconnect_callback, + &sent_callback, + &sent_with_reply_callback); + + ExecuteCreateSenderAndReceiverSequence(true, &state_callback); bool first_message = true; - SendAcquireRequestAndExpectResponse(acquire_callback, &sender_ptr, first_message, false); + SendAcquireRequestAndExpectResponse(sent_callback, &sender_ptr, first_message, false); + ExpectServerDestruction(receiver_ptr); + ExpectClientDestruction(sender_ptr); } TEST_F(DatarouterMessageClientFixture, SecondAcquireRequestShouldNotSetMwsrReader) @@ -536,18 +688,33 @@ TEST_F(DatarouterMessageClientFixture, SecondAcquireRequestShouldNotSetMwsrReade testing::InSequence order_matters; - score::mw::com::message_passing::SenderMock* sender_ptr{}; - score::mw::com::message_passing::ReceiverMock* receiver_ptr{}; - score::mw::com::message_passing::IReceiver::ShortMessageReceivedCallback acquire_callback; - ExpectSenderAndReceiverCreation(&receiver_ptr, &sender_ptr, {}, &acquire_callback); - - ExecuteCreateSenderAndReceiverSequence(); + score::message_passing::ClientConnectionMock* sender_ptr{}; + score::message_passing::ServerMock* receiver_ptr{}; + score::message_passing::ConnectCallback connect_callback; + score::message_passing::DisconnectCallback disconnect_callback; + score::message_passing::MessageCallback sent_callback; + score::message_passing::MessageCallback sent_with_reply_callback; + score::message_passing::IClientConnection::StateCallback state_callback; + + ExpectSenderAndReceiverCreation(&receiver_ptr, + &sender_ptr, + &state_callback, + nullptr, + {}, + &connect_callback, + &disconnect_callback, + &sent_callback, + &sent_with_reply_callback); + + ExecuteCreateSenderAndReceiverSequence(true, &state_callback); bool first_message = true; - SendAcquireRequestAndExpectResponse(acquire_callback, &sender_ptr, first_message, false); + SendAcquireRequestAndExpectResponse(sent_callback, &sender_ptr, first_message, false); first_message = false; - SendAcquireRequestAndExpectResponse(acquire_callback, &sender_ptr, first_message); + SendAcquireRequestAndExpectResponse(sent_callback, &sender_ptr, first_message); + ExpectServerDestruction(receiver_ptr); + ExpectClientDestruction(sender_ptr); } // Refactor to acquire request @@ -560,12 +727,24 @@ TEST_F(DatarouterMessageClientFixture, ClientShouldShutdownAfterFailingToSendMes testing::InSequence order_matters; - score::mw::com::message_passing::SenderMock* sender_ptr{}; - score::mw::com::message_passing::ReceiverMock* receiver_ptr{}; - score::mw::com::message_passing::IReceiver::ShortMessageReceivedCallback acquire_callback; - ExpectSenderAndReceiverCreation(&receiver_ptr, &sender_ptr, {}, &acquire_callback); - - ExecuteCreateSenderAndReceiverSequence(); + score::message_passing::ClientConnectionMock* sender_ptr{}; + score::message_passing::ServerMock* receiver_ptr{}; + score::message_passing::ConnectCallback connect_callback; + score::message_passing::DisconnectCallback disconnect_callback; + score::message_passing::MessageCallback sent_callback; + score::message_passing::MessageCallback sent_with_reply_callback; + score::message_passing::IClientConnection::StateCallback state_callback; + ExpectSenderAndReceiverCreation(&receiver_ptr, + &sender_ptr, + &state_callback, + nullptr, + {}, + &connect_callback, + &disconnect_callback, + &sent_callback, + &sent_with_reply_callback); + + ExecuteCreateSenderAndReceiverSequence(true, &state_callback); auto send_error = score::cpp::make_unexpected(score::os::Error::createFromErrno()); @@ -575,7 +754,11 @@ TEST_F(DatarouterMessageClientFixture, ClientShouldShutdownAfterFailingToSendMes ExpectUnlinkMwsrWriterFile(); ExpectSendAcquireResponse(sender_ptr, result, send_error); - acquire_callback(score::mw::com::message_passing::ShortMessagePayload{}, {}); + score::message_passing::ServerConnectionMock connection; + const score::cpp::span message{}; + sent_callback(connection, message); + ExpectServerDestruction(receiver_ptr); + ExpectClientDestruction(sender_ptr); } TEST_F(DatarouterMessageClientFixture, RunShouldSetupAndConnect) @@ -587,11 +770,23 @@ TEST_F(DatarouterMessageClientFixture, RunShouldSetupAndConnect) testing::InSequence order_matters; - score::mw::com::message_passing::SenderMock* sender_ptr{}; - ExpectSenderAndReceiverCreation(nullptr, &sender_ptr); + score::message_passing::ClientConnectionMock* sender_ptr{}; + score::message_passing::ServerMock* receiver_ptr{}; + score::message_passing::IClientConnection::StateCallback state_callback; + std::promise callback_registered; + + ExpectSenderAndReceiverCreation(&receiver_ptr, &sender_ptr, &state_callback, &callback_registered); + ExpectSendConnectMessage(sender_ptr); + ExpectServerDestruction(receiver_ptr); + ExpectClientDestruction(sender_ptr); ExpectUnlinkMwsrWriterFile(); client_->Run(); + + callback_registered.get_future().wait(); + + state_callback(score::message_passing::IClientConnection::State::kReady); + client_->Shutdown(); } @@ -603,13 +798,24 @@ TEST_F(DatarouterMessageClientFixture, RunShallNotBeCalledMoreThanOnce) RecordProperty("DerivationTechnique", "Generation and analysis of equivalence classes"); testing::InSequence order_matters; - score::mw::com::message_passing::SenderMock* sender_ptr{}; - ExpectSenderAndReceiverCreation(nullptr, &sender_ptr); + + score::message_passing::ClientConnectionMock* sender_ptr{}; + score::message_passing::ServerMock* receiver_ptr{}; + score::message_passing::IClientConnection::StateCallback state_callback; + std::promise callback_registered; + + ExpectSenderAndReceiverCreation(&receiver_ptr, &sender_ptr, &state_callback, &callback_registered); ExpectSendConnectMessage(sender_ptr); + + ExpectServerDestruction(receiver_ptr); + ExpectClientDestruction(sender_ptr); ExpectUnlinkMwsrWriterFile(); client_->Run(); + callback_registered.get_future().wait(); + + state_callback(score::message_passing::IClientConnection::State::kReady); EXPECT_DEATH(client_->Run(), ""); } @@ -649,11 +855,20 @@ TEST_F(DatarouterMessageClientFixture, FailedToChownOwnMsrWriterFileForDataRoute testing::InSequence order_matters; - score::mw::com::message_passing::SenderMock* sender_ptr{}; - ExpectSenderAndReceiverCreation(nullptr, &sender_ptr); + score::message_passing::ClientConnectionMock* sender_ptr{}; + score::message_passing::ServerMock* receiver_ptr{}; + score::message_passing::IClientConnection::StateCallback state_callback; + std::promise callback_registered; + + ExpectSenderAndReceiverCreation(&receiver_ptr, &sender_ptr, &state_callback, &callback_registered); ExpectSendConnectMessage(sender_ptr); + ExpectServerDestruction(receiver_ptr); + ExpectClientDestruction(sender_ptr); ExpectUnlinkMwsrWriterFile(false); client_->Run(); + + callback_registered.get_future().wait(); + state_callback(score::message_passing::IClientConnection::State::kReady); client_->Shutdown(); } @@ -667,9 +882,24 @@ TEST_F(DatarouterMessageClientFixture, GivenExitRequestDuringConnectionShouldNot testing::InSequence order_matters; - ExpectSenderAndReceiverCreation(); - ExpectUnlinkMwsrWriterFile(); + score::message_passing::ClientConnectionMock* sender_ptr{}; + score::message_passing::ServerMock* receiver_ptr{}; + + ExpectSenderAndReceiverCreation(&receiver_ptr, + &sender_ptr, + nullptr, + nullptr, + score::cpp::make_unexpected(score::os::Error::createFromErrno()), + nullptr, + nullptr, + nullptr, + nullptr, + true, + false); + ExpectUnlinkMwsrWriterFile(); + ExpectServerDestruction(receiver_ptr); + ExpectClientDestruction(sender_ptr); client_->SetupReceiver(); stop_source_.request_stop(); client_->ConnectToDatarouter(); @@ -684,12 +914,35 @@ TEST_F(DatarouterMessageClientFixture, FailedToEmptySignalSet) testing::InSequence order_matters; - ExpectSenderAndReceiverCreation( - nullptr, nullptr, score::cpp::make_unexpected(score::os::Error::createFromErrno()), nullptr, false); + score::message_passing::ClientConnectionMock* sender_ptr{}; + score::message_passing::ServerMock* receiver_ptr{}; + score::message_passing::IClientConnection::StateCallback state_callback; + std::promise callback_registered; + + ExpectSenderAndReceiverCreation(&receiver_ptr, + &sender_ptr, + &state_callback, + &callback_registered, + score::cpp::make_unexpected(score::os::Error::createFromErrno()), + nullptr, + nullptr, + nullptr, + nullptr, + false); ExpectUnlinkMwsrWriterFile(); + ExpectServerDestruction(receiver_ptr); + ExpectClientDestruction(sender_ptr); + client_->SetupReceiver(); - client_->ConnectToDatarouter(); + // We need to unblock waiting for the connection so we change the state in a separate thread + std::thread connect_thread([this]() noexcept { + client_->ConnectToDatarouter(); + }); + callback_registered.get_future().wait(); + + state_callback(score::message_passing::IClientConnection::State::kReady); + connect_thread.join(); } } // namespace diff --git a/score/mw/log/detail/data_router/data_router_messages.cpp b/score/mw/log/detail/data_router/data_router_messages.cpp index 2de8472..5c5a72b 100644 --- a/score/mw/log/detail/data_router/data_router_messages.cpp +++ b/score/mw/log/detail/data_router/data_router_messages.cpp @@ -36,11 +36,6 @@ bool operator!=(const ConnectMessageFromClient& lhs, const ConnectMessageFromCli return !(lhs == rhs); } -score::mw::com::message_passing::MessageId ToMessageId(const DatarouterMessageIdentifier& message_id) noexcept -{ - return static_cast>(message_id); -} - ConnectMessageFromClient::ConnectMessageFromClient(const LoggingIdentifier& appid, uid_t uid, bool use_dynamic_identifier, diff --git a/score/mw/log/detail/data_router/data_router_messages.h b/score/mw/log/detail/data_router/data_router_messages.h index de49ca8..19fe9c0 100644 --- a/score/mw/log/detail/data_router/data_router_messages.h +++ b/score/mw/log/detail/data_router/data_router_messages.h @@ -14,11 +14,13 @@ #ifndef SCORE_MW_LOG_DETAIL_DATA_ROUTER_MESSAGES_H #define SCORE_MW_LOG_DETAIL_DATA_ROUTER_MESSAGES_H -#include "score/mw/com/message_passing/message.h" - #include "score/mw/log/detail/logging_identifier.h" +#include + #include +#include +#include namespace score { @@ -29,14 +31,43 @@ namespace log namespace detail { -enum class DatarouterMessageIdentifier : score::mw::com::message_passing::MessageId +enum class DatarouterMessageIdentifier : std::uint8_t { kConnect = 0x00, kAcquireRequest = 0x01, kAcquireResponse = 0x02, }; -score::mw::com::message_passing::MessageId ToMessageId(const DatarouterMessageIdentifier& message_id) noexcept; +/// \brief Returns a pointer to the raw memory of a trivially copyable object as uint8_t*. +/// \tparam T The type of the object. Must be trivially copyable. +/// \param obj The object to get the memory pointer for. +/// \returns A pointer to the object's memory as uint8_t*. +template +constexpr const std::uint8_t* AsBytes(const T& obj) noexcept +{ + static_assert(std::is_trivially_copyable::value, "T must be trivially copyable"); + return static_cast(static_cast(std::addressof(obj))); +} + +/// \brief Serializes a trivially copyable message with a message identifier prefix. +/// \tparam MessageType The type of the message payload. Must be trivially copyable. +/// \param identifier The message identifier to prepend to the serialized data. +/// \param payload The message payload to serialize. +/// \returns A std::array containing the message identifier followed by the serialized payload. +template +constexpr std::array SerializeMessage( + const DatarouterMessageIdentifier identifier, + const MessageType& payload) noexcept +{ + static_assert(std::is_trivially_copyable::value, "MessageType must be trivially copyable"); + + std::array message{}; + message[0] = score::cpp::to_underlying(identifier); + auto destination_iterator = message.begin(); + std::advance(destination_iterator, 1U); + std::ignore = std::copy_n(AsBytes(payload), sizeof(MessageType), destination_iterator); + return message; +} class ConnectMessageFromClient { diff --git a/score/mw/log/detail/data_router/message_passing_config.h b/score/mw/log/detail/data_router/message_passing_config.h new file mode 100644 index 0000000..7242bd9 --- /dev/null +++ b/score/mw/log/detail/data_router/message_passing_config.h @@ -0,0 +1,63 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#ifndef SCORE_MW_LOG_DETAIL_DATA_ROUTER_MESSAGE_PASSING_CONFIG_H +#define SCORE_MW_LOG_DETAIL_DATA_ROUTER_MESSAGE_PASSING_CONFIG_H + +#include + +namespace score +{ +namespace mw +{ +namespace log +{ +namespace detail +{ + +/// \brief Configuration constants for message passing between DataRouter and logging clients. +struct MessagePassingConfig +{ + /// \brief Maximum message size in bytes (1 byte for message ID + 16 bytes for payload). + static constexpr std::uint32_t kMaxMessageSize{17U}; + + /// \brief Maximum number of messages in receiver queue. + /// \note Value not used at the moment of integration with message passing library. May change in the future. + static constexpr std::uint32_t kMaxReceiverQueueSize{0U}; + + /// \brief Number of pre-allocated connections. + static constexpr std::uint32_t kPreAllocConnections{0U}; + + /// \brief Maximum number of queued notifications. + static constexpr std::uint32_t kMaxQueuedNotifies{0U}; + + /// \brief Maximum reply size in bytes. + static constexpr std::uint32_t kMaxReplySize{0U}; + + /// \brief Maximum notification size in bytes. + static constexpr std::uint32_t kMaxNotifySize{0U}; + + /// \brief The DataRouter receiver endpoint identifier. + static constexpr const char* kDatarouterReceiverIdentifier{"/logging.datarouter_recv"}; + + /// \brief Start index of random filename part in the shared memory file name. + /// \note The file name format is "/logging.shm_XXXXXX" where X is random. + static constexpr std::size_t kRandomFilenameStartIndex{13U}; +}; + +} // namespace detail +} // namespace log +} // namespace mw +} // namespace score + +#endif // SCORE_MW_LOG_DETAIL_DATA_ROUTER_MESSAGE_PASSING_CONFIG_H diff --git a/score/mw/log/detail/data_router/message_passing_factory.h b/score/mw/log/detail/data_router/message_passing_factory.h index 2870cf5..c14a4aa 100644 --- a/score/mw/log/detail/data_router/message_passing_factory.h +++ b/score/mw/log/detail/data_router/message_passing_factory.h @@ -15,11 +15,11 @@ #define SCORE_MW_LOG_DETAIL_DATA_ROUTER_MESSAGE_PASSING_FACTORY_H #include "score/span.hpp" -#include "score/concurrency/executor.h" -#include "score/mw/com/message_passing/i_receiver.h" -#include "score/mw/com/message_passing/i_sender.h" -#include "score/mw/com/message_passing/receiver_config.h" -#include "score/mw/com/message_passing/sender_config.h" +#include "score/message_passing/i_client_connection.h" +#include "score/message_passing/i_client_factory.h" +#include "score/message_passing/i_server.h" +#include "score/message_passing/i_server_factory.h" +#include "score/message_passing/service_protocol_config.h" #include #include @@ -43,19 +43,13 @@ class MessagePassingFactory MessagePassingFactory& operator=(const MessagePassingFactory&) = delete; MessagePassingFactory& operator=(MessagePassingFactory&&) = delete; - virtual score::cpp::pmr::unique_ptr CreateReceiver( - const std::string_view identifier, - concurrency::Executor& executor, - const score::cpp::span allowed_user_ids, - const score::mw::com::message_passing::ReceiverConfig& receiver_config, - score::cpp::pmr::memory_resource* memory_resource) = 0; + virtual score::cpp::pmr::unique_ptr CreateServer( + const score::message_passing::ServiceProtocolConfig& protocol_config, + const score::message_passing::IServerFactory::ServerConfig& server_config) = 0; - virtual score::cpp::pmr::unique_ptr CreateSender( - const std::string_view identifier, - const score::cpp::stop_token& token, - const score::mw::com::message_passing::SenderConfig& sender_config, - score::mw::com::message_passing::LoggingCallback callback, - score::cpp::pmr::memory_resource* memory_resource) = 0; + virtual score::cpp::pmr::unique_ptr CreateClient( + const score::message_passing::ServiceProtocolConfig& protocol_config, + const score::message_passing::IClientFactory::ClientConfig& client_config) = 0; }; } // namespace detail diff --git a/score/mw/log/detail/data_router/message_passing_factory_impl.cpp b/score/mw/log/detail/data_router/message_passing_factory_impl.cpp index 426ffe2..cf32b98 100644 --- a/score/mw/log/detail/data_router/message_passing_factory_impl.cpp +++ b/score/mw/log/detail/data_router/message_passing_factory_impl.cpp @@ -13,9 +13,6 @@ #include "score/mw/log/detail/data_router/message_passing_factory_impl.h" -#include "score/mw/com/message_passing/receiver_factory_impl.h" -#include "score/mw/com/message_passing/sender_factory_impl.h" - namespace score { namespace mw @@ -25,26 +22,22 @@ namespace log namespace detail { -score::cpp::pmr::unique_ptr MessagePassingFactoryImpl::CreateReceiver( - const std::string_view identifier, - concurrency::Executor& executor, - const score::cpp::span allowed_user_ids, - const score::mw::com::message_passing::ReceiverConfig& receiver_config, - score::cpp::pmr::memory_resource* const monotonic_memory_resource) noexcept +MessagePassingFactoryImpl::MessagePassingFactoryImpl() : server_factory_{}, client_factory_{server_factory_.GetEngine()} +{ +} + +score::cpp::pmr::unique_ptr MessagePassingFactoryImpl::CreateServer( + const score::message_passing::ServiceProtocolConfig& protocol_config, + const score::message_passing::IServerFactory::ServerConfig& server_config) noexcept { - return score::mw::com::message_passing::ReceiverFactoryImpl::Create( - identifier, executor, allowed_user_ids, receiver_config, monotonic_memory_resource); + return server_factory_.Create(protocol_config, server_config); } -score::cpp::pmr::unique_ptr MessagePassingFactoryImpl::CreateSender( - const std::string_view identifier, - const score::cpp::stop_token& token, - const score::mw::com::message_passing::SenderConfig& sender_config, - score::mw::com::message_passing::LoggingCallback callback, - score::cpp::pmr::memory_resource* const monotonic_memory_resource) noexcept +score::cpp::pmr::unique_ptr MessagePassingFactoryImpl::CreateClient( + const score::message_passing::ServiceProtocolConfig& protocol_config, + const score::message_passing::IClientFactory::ClientConfig& client_config) noexcept { - return score::mw::com::message_passing::SenderFactoryImpl::Create( - identifier, token, sender_config, std::move(callback), monotonic_memory_resource); + return client_factory_.Create(protocol_config, client_config); } } // namespace detail diff --git a/score/mw/log/detail/data_router/message_passing_factory_impl.h b/score/mw/log/detail/data_router/message_passing_factory_impl.h index 2f147f1..bddcc0d 100644 --- a/score/mw/log/detail/data_router/message_passing_factory_impl.h +++ b/score/mw/log/detail/data_router/message_passing_factory_impl.h @@ -16,6 +16,20 @@ #include "score/mw/log/detail/data_router/message_passing_factory.h" +#include "score/message_passing/i_client_factory.h" +#include "score/message_passing/i_server_factory.h" +#include + +#ifdef __QNX__ +#include "score/message_passing/qnx_dispatch/qnx_dispatch_client_factory.h" +#include "score/message_passing/qnx_dispatch/qnx_dispatch_engine.h" +#include "score/message_passing/qnx_dispatch/qnx_dispatch_server_factory.h" +#else +#include "score/message_passing/unix_domain/unix_domain_client_factory.h" +#include "score/message_passing/unix_domain/unix_domain_engine.h" +#include "score/message_passing/unix_domain/unix_domain_server_factory.h" +#endif + namespace score { namespace mw @@ -25,22 +39,43 @@ namespace log namespace detail { +/* + Deviation from Rule A16-0-1: + - Rule A16-0-1 (required, implementation, automated) + The pre-processor shall only be used for unconditional and conditional file + inclusion and include guards, and using the following directives: (1) #ifndef, + #ifdef, (3) #if, (4) #if defined, (5) #elif, (6) #else, (7) #define, (8) #endif, (9) + #include. + Justification: + - Feature Flag to enable/disable Logging Modes in Production SW. + */ +// coverity[autosar_cpp14_a16_0_1_violation] see above +#ifdef __QNX__ +using ServerFactory = score::message_passing::QnxDispatchServerFactory; +using ClientFactory = score::message_passing::QnxDispatchClientFactory; +// coverity[autosar_cpp14_a16_0_1_violation] see above +#else +using ServerFactory = score::message_passing::UnixDomainServerFactory; +using ClientFactory = score::message_passing::UnixDomainClientFactory; +// coverity[autosar_cpp14_a16_0_1_violation] see above +#endif + class MessagePassingFactoryImpl : public MessagePassingFactory { public: - score::cpp::pmr::unique_ptr CreateReceiver( - const std::string_view identifier, - concurrency::Executor& executor, - const score::cpp::span allowed_user_ids, - const score::mw::com::message_passing::ReceiverConfig& receiver_config, - score::cpp::pmr::memory_resource* const monotonic_memory_resource) noexcept override; - - score::cpp::pmr::unique_ptr CreateSender( - const std::string_view identifier, - const score::cpp::stop_token& token, - const score::mw::com::message_passing::SenderConfig& sender_config, - score::mw::com::message_passing::LoggingCallback callback, - score::cpp::pmr::memory_resource* const monotonic_memory_resource) noexcept override; + explicit MessagePassingFactoryImpl(); + + score::cpp::pmr::unique_ptr CreateServer( + const score::message_passing::ServiceProtocolConfig& protocol_config, + const score::message_passing::IServerFactory::ServerConfig& server_config) noexcept override; + + score::cpp::pmr::unique_ptr CreateClient( + const score::message_passing::ServiceProtocolConfig& protocol_config, + const score::message_passing::IClientFactory::ClientConfig& client_config) noexcept override; + + private: + ServerFactory server_factory_; + ClientFactory client_factory_; }; } // namespace detail diff --git a/score/mw/log/detail/data_router/message_passing_factory_mock.h b/score/mw/log/detail/data_router/message_passing_factory_mock.h index 18344bd..748bf13 100644 --- a/score/mw/log/detail/data_router/message_passing_factory_mock.h +++ b/score/mw/log/detail/data_router/message_passing_factory_mock.h @@ -30,22 +30,16 @@ namespace detail class MessagePassingFactoryMock : public MessagePassingFactory { public: - MOCK_METHOD((score::cpp::pmr::unique_ptr), - CreateReceiver, - (const std::string_view, - concurrency::Executor&, - const score::cpp::span, - const score::mw::com::message_passing::ReceiverConfig&, - score::cpp::pmr::memory_resource*), + MOCK_METHOD((score::cpp::pmr::unique_ptr), + CreateServer, + (const score::message_passing::ServiceProtocolConfig& protocol_config, + const score::message_passing::IServerFactory::ServerConfig& server_config), (override)); - MOCK_METHOD((score::cpp::pmr::unique_ptr), - CreateSender, - (const std::string_view identifier, - const score::cpp::stop_token& token, - const score::mw::com::message_passing::SenderConfig& sender_config, - score::mw::com::message_passing::LoggingCallback callback, - score::cpp::pmr::memory_resource* memory_resource), + MOCK_METHOD((score::cpp::pmr::unique_ptr), + CreateClient, + (const score::message_passing::ServiceProtocolConfig& protocol_config, + const score::message_passing::IClientFactory::ClientConfig& client_config), (override)); }; diff --git a/score/mw/log/detail/data_router/message_passing_factory_test.cpp b/score/mw/log/detail/data_router/message_passing_factory_test.cpp index 0b1377d..ae4daa8 100644 --- a/score/mw/log/detail/data_router/message_passing_factory_test.cpp +++ b/score/mw/log/detail/data_router/message_passing_factory_test.cpp @@ -39,9 +39,10 @@ TEST(MessagePassingFactoryTests, CreateReceiverShouldReturnValue) MessagePassingFactoryImpl factory{}; score::concurrency::ThreadPool thread_pool{2}; - score::mw::com::message_passing::ReceiverConfig receiver_config{}; - auto receiver = - factory.CreateReceiver(kIdentifier, thread_pool, {}, receiver_config, score::cpp::pmr::get_default_resource()); + const score::message_passing::ServiceProtocolConfig service_protocol_config{}; + + const score::message_passing::IServerFactory::ServerConfig server_config{}; + auto receiver = factory.CreateServer(service_protocol_config, server_config); EXPECT_NE(receiver, nullptr); } @@ -54,14 +55,10 @@ TEST(MessagePassingFactoryTests, CreateSenderShouldReturnValue) MessagePassingFactoryImpl factory{}; - score::cpp::stop_source s{}; - s.request_stop(); - const score::mw::com::message_passing::SenderConfig sender_config{}; - auto sender = factory.CreateSender(kIdentifier, - s.get_token(), - sender_config, - &score::mw::com::message_passing::DefaultLoggingCallback, - score::cpp::pmr::get_default_resource()); + const score::message_passing::ServiceProtocolConfig& protocol_config{kIdentifier, 9U, 0U, 0U}; + const score::message_passing::IClientFactory::ClientConfig& client_config{0, 0, false, false, false}; + + auto sender = factory.CreateClient(protocol_config, client_config); EXPECT_NE(sender, nullptr); } diff --git a/score/mw/log/detail/flags/BUILD b/score/mw/log/detail/flags/BUILD index 9005aee..22b7be9 100644 --- a/score/mw/log/detail/flags/BUILD +++ b/score/mw/log/detail/flags/BUILD @@ -44,19 +44,3 @@ config_setting( "@score_baselibs//score/mw/log:__subpackages__", ], ) - -bool_flag( - name = "KUse_Stub_Implementation_Only", - build_setting_default = False, -) - -config_setting( - name = "config_KUse_Stub_Implementation_Only", - flag_values = { - ":KUse_Stub_Implementation_Only": "True", - }, - visibility = [ - "//score/mw/log:__subpackages__", - "@score_baselibs//score/mw/log:__subpackages__", - ], -) diff --git a/score/mw/log/detail/utils/signal_handling/BUILD b/score/mw/log/detail/utils/signal_handling/BUILD new file mode 100644 index 0000000..43d75fe --- /dev/null +++ b/score/mw/log/detail/utils/signal_handling/BUILD @@ -0,0 +1,37 @@ +# ******************************************************************************* +# Copyright (c) 2025 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +COMPILER_WARNING_FEATURES = [ + "treat_warnings_as_errors", + "additional_warnings", + "strict_warnings", +] + +cc_library( + name = "signal_handling", + srcs = [ + "signal_handling.cpp", + ], + hdrs = [ + "signal_handling.h", + ], + features = COMPILER_WARNING_FEATURES, + tags = ["FFI"], + visibility = [ + "//score/mw/log:__subpackages__", + "@score_baselibs//score/mw/log:__subpackages__", + ], + deps = [ + "@score_baselibs//score/os/utils:signal", + ], +) diff --git a/score/mw/log/detail/utils/signal_handling/signal_handling.cpp b/score/mw/log/detail/utils/signal_handling/signal_handling.cpp new file mode 100644 index 0000000..e23e1f6 --- /dev/null +++ b/score/mw/log/detail/utils/signal_handling/signal_handling.cpp @@ -0,0 +1,59 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "score/mw/log/detail/utils/signal_handling/signal_handling.h" + +namespace score::mw::log::detail +{ + +score::cpp::expected SignalHandling::PThreadBlockSigTerm(score::os::Signal& signal) noexcept +{ + sigset_t sig_set; + + score::cpp::expected return_error_result{}; + auto result = signal.SigEmptySet(sig_set); + if (result.has_value()) + { + // signal handling is tolerated by design. Argumentation: Ticket-101432 + // coverity[autosar_cpp14_m18_7_1_violation] + result = signal.SigAddSet(sig_set, SIGTERM); + if (result.has_value()) + { + /* NOLINTNEXTLINE(score-banned-function) using PthreadSigMask by design. Argumentation: Ticket-101432 */ + result = signal.PthreadSigMask(SIG_BLOCK, sig_set); + } + } + return result; +} + +score::cpp::expected SignalHandling::PThreadUnblockSigTerm(score::os::Signal& signal) noexcept +{ + sigset_t sig_set; + + score::cpp::expected return_error_result{}; + auto result = signal.SigEmptySet(sig_set); + if (result.has_value()) + { + // signal handling is tolerated by design. Argumentation: Ticket-101432 + // coverity[autosar_cpp14_m18_7_1_violation] + result = signal.SigAddSet(sig_set, SIGTERM); + if (result.has_value()) + { + /* NOLINTNEXTLINE(score-banned-function) using PthreadSigMask by design. Argumentation: Ticket-101432 */ + result = signal.PthreadSigMask(SIG_UNBLOCK, sig_set); + } + } + return result; +} + +} // namespace score::mw::log::detail diff --git a/score/mw/log/detail/utils/signal_handling/signal_handling.h b/score/mw/log/detail/utils/signal_handling/signal_handling.h new file mode 100644 index 0000000..f53276a --- /dev/null +++ b/score/mw/log/detail/utils/signal_handling/signal_handling.h @@ -0,0 +1,58 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#ifndef SCORE_MW_LOG_DETAIL_UTILS_SIGNAL_HANDLING_SIGNAL_HANDLING_H_ +#define SCORE_MW_LOG_DETAIL_UTILS_SIGNAL_HANDLING_SIGNAL_HANDLING_H_ + +#include "score/os/utils/signal.h" + +namespace score::mw::log::detail +{ + +class SignalHandling +{ + public: + /// \brief Blocks the SIGTERM signal for the current thread. + /// \param signal The signal interface to use for blocking. + /// \returns The result of the pthread_sigmask call, or an error if the operation failed. + static score::cpp::expected PThreadBlockSigTerm(score::os::Signal& signal) noexcept; + + /// \brief Unblocks the SIGTERM signal for the current thread. + /// \param signal The signal interface to use for unblocking. + /// \returns The result of the pthread_sigmask call, or an error if the operation failed. + static score::cpp::expected PThreadUnblockSigTerm(score::os::Signal& signal) noexcept; + + /// \brief Executes a function with SIGTERM blocked for the current thread. + /// \details This is a RAII-style helper that blocks SIGTERM before executing the provided function + /// and automatically unblocks it after the function completes (regardless of the outcome). + /// This is useful for protecting critical sections from being interrupted by termination signals. + /// \tparam Func The type of the callable to execute. + /// \param signal The signal interface to use for blocking/unblocking. + /// \param func The callable to execute while SIGTERM is blocked e.g. creating a new thread which should not + /// intercept SIGTERM handling. + /// \returns The result of the provided callable. + /// \note Errors from blocking/unblocking operations are silently ignored. + template + static auto WithSigTermBlocked(score::os::Signal& signal, Func&& func) noexcept -> decltype(func()) + { + const auto block_result = PThreadBlockSigTerm(signal); + auto result = func(); + const auto unblock_result = PThreadUnblockSigTerm(signal); + std::ignore = unblock_result; + return result; + } +}; + +} // namespace score::mw::log::detail + +#endif // SCORE_MW_LOG_DETAIL_UTILS_SIGNAL_HANDLING_SIGNAL_HANDLING_H_ diff --git a/score/mw/log/legacy_non_verbose_api/BUILD b/score/mw/log/legacy_non_verbose_api/BUILD index 6b07f5c..3635582 100644 --- a/score/mw/log/legacy_non_verbose_api/BUILD +++ b/score/mw/log/legacy_non_verbose_api/BUILD @@ -70,7 +70,6 @@ cc_test( "@score_baselibs//score/os/mocklib:stdlib_mock", "@score_baselibs//score/os/mocklib:unistd_mock", "@score_baselibs//score/os/utils/mocklib:signal_mock", - "@score_communication//score/mw/com/message_passing:mock", ], ) diff --git a/score/mw/log/legacy_non_verbose_api/tracing_test.cpp b/score/mw/log/legacy_non_verbose_api/tracing_test.cpp index 49228bb..c7e413f 100644 --- a/score/mw/log/legacy_non_verbose_api/tracing_test.cpp +++ b/score/mw/log/legacy_non_verbose_api/tracing_test.cpp @@ -403,6 +403,76 @@ TEST_F(LoggerFixture, WhenTypeRegistrationFailsDroppedLogsCounterIsIncremented) EXPECT_EQ(static_cast(kNumberOfLogAttempts), log_entry_instance.get_dropped_logs_count()); } +TEST(TraceFixtureTest, TraceFatalFunctionCallsTraceLevel) +{ + RecordProperty("Requirement", "SCR-1633147"); + RecordProperty("ASIL", "B"); + RecordProperty("Description", "Verifies that TRACE_FATAL calls TRACE_LEVEL with kFatal level"); + RecordProperty("TestingTechnique", "Requirements-based test"); + RecordProperty("DerivationTechnique", "Analysis of requirements"); + + LogEntry entry{}; + auto& logger = LOG_ENTRY(); + TRACE_FATAL(entry); + EXPECT_EQ(true, logger.enabled_at(score::platform::LogLevel::kFatal)); +} + +TEST(TraceFixtureTest, TraceWarnFunctionCallsTraceLevel) +{ + RecordProperty("Requirement", "SCR-1633147"); + RecordProperty("ASIL", "B"); + RecordProperty("Description", "Verifies that TRACE_WARN calls TRACE_LEVEL with kWarn level"); + RecordProperty("TestingTechnique", "Requirements-based test"); + RecordProperty("DerivationTechnique", "Analysis of requirements"); + + LogEntry entry{}; + auto& logger = LOG_ENTRY(); + TRACE_WARN(entry); + EXPECT_EQ(true, logger.enabled_at(score::platform::LogLevel::kWarn)); +} + +TEST(TraceFixtureTest, TraceVerboseFunctionCallsTraceLevel) +{ + RecordProperty("Requirement", "SCR-1633147"); + RecordProperty("ASIL", "B"); + RecordProperty("Description", "Verifies that TRACE_VERBOSE calls TRACE_LEVEL with kVerbose level"); + RecordProperty("TestingTechnique", "Requirements-based test"); + RecordProperty("DerivationTechnique", "Analysis of requirements"); + + LogEntry entry{}; + auto& logger = LOG_ENTRY(); + TRACE_VERBOSE(entry); + EXPECT_EQ(true, logger.enabled_at(score::platform::LogLevel::kVerbose)); +} + +TEST(TraceFixtureTest, TraceDebugFunctionCallsTraceLevel) +{ + RecordProperty("Requirement", "SCR-1633147"); + RecordProperty("ASIL", "B"); + RecordProperty("Description", "Verifies that TRACE_DEBUG calls TRACE_LEVEL with kDebug level"); + RecordProperty("TestingTechnique", "Requirements-based test"); + RecordProperty("DerivationTechnique", "Analysis of requirements"); + + LogEntry entry{}; + auto& logger = LOG_ENTRY(); + TRACE_DEBUG(entry); + EXPECT_EQ(true, logger.enabled_at(score::platform::LogLevel::kDebug)); +} + +TEST(TraceFixtureTest, TraceInfoFunctionCallsTraceLevel) +{ + RecordProperty("Requirement", "SCR-1633147"); + RecordProperty("ASIL", "B"); + RecordProperty("Description", "Verifies that TRACE_INFO calls TRACE_LEVEL with kInfo level"); + RecordProperty("TestingTechnique", "Requirements-based test"); + RecordProperty("DerivationTechnique", "Analysis of requirements"); + + LogEntry entry{}; + auto& logger = LOG_ENTRY(); + TRACE_INFO(entry); + EXPECT_EQ(true, logger.enabled_at(score::platform::LogLevel::kInfo)); +} + } // namespace } // namespace detail } // namespace log diff --git a/score/mw/log/test/console_logging_environment/console_logging_environment.cpp b/score/mw/log/test/console_logging_environment/console_logging_environment.cpp index d003d0b..e4858e3 100644 --- a/score/mw/log/test/console_logging_environment/console_logging_environment.cpp +++ b/score/mw/log/test/console_logging_environment/console_logging_environment.cpp @@ -12,11 +12,7 @@ ********************************************************************************/ #include "score/mw/log/test/console_logging_environment/console_logging_environment.h" -#if (!defined KUSE_STUB_IMPLEMENTATION_ONLY) #include "score/mw/log/detail/common/recorder_factory.h" -#else -#include "score/mw/log/detail/recorder_factory_stub.h" -#endif #include "score/mw/log/runtime.h" @@ -29,14 +25,10 @@ namespace log void ConsoleLoggingEnvironment::SetUp() { -#if (!defined KUSE_STUB_IMPLEMENTATION_ONLY) score::mw::log::detail::Configuration config{}; config.SetLogMode({score::mw::LogMode::kConsole}); config.SetDefaultConsoleLogLevel(score::mw::log::LogLevel::kVerbose); recorder_ = score::mw::log::detail::RecorderFactory().CreateRecorderFromLogMode(score::mw::LogMode::kConsole, config); -#else - recorder_ = score::mw::log::detail::RecorderFactory().CreateWithConsoleLoggingOnly(nullptr); -#endif score::mw::log::detail::Runtime::SetRecorder(recorder_.get()); } diff --git a/score/mw/log/test/fake_recorder/BUILD b/score/mw/log/test/fake_recorder/BUILD new file mode 100644 index 0000000..353eeaa --- /dev/null +++ b/score/mw/log/test/fake_recorder/BUILD @@ -0,0 +1,28 @@ +# ******************************************************************************* +# Copyright (c) 2025 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") + +cc_library( + name = "fake_recorder", + testonly = True, + srcs = ["fake_recorder.cpp"], + hdrs = ["fake_recorder.h"], + features = COMPILER_WARNING_FEATURES, + visibility = ["//visibility:public"], + deps = [ + "//score/datarouter/lib/synchronized:synchronized_utility", + "@score_baselibs//score/language/futurecpp", + "@score_baselibs//score/mw/log:recorder", + ], +) diff --git a/score/mw/log/test/fake_recorder/README.md b/score/mw/log/test/fake_recorder/README.md new file mode 100644 index 0000000..c06c54c --- /dev/null +++ b/score/mw/log/test/fake_recorder/README.md @@ -0,0 +1,65 @@ +# FakeRecorder – How to use in Unit Tests + +`FakeRecorder` is a test implementation of `score::mw::log::Recorder` used in unit tests. +When enabled, logging output is routed to **stdout**, which allows tests to validate log output using GTest's `testing::internal::CaptureStdout()`. + +## Enable FakeRecorder in your test target (BUILD) + +Add this dependency to your unit test target: + +```bzl +deps = [ + "//score/mw/log/test/fake_recorder_environment:auto_register_fake_recorder_env", +] +``` + +This target installs `FakeRecorder` via a **GTest Environment**, so most tests do not need to include any FakeRecorder headers or manually configure the recorder. + +**Recommended**: Do not include FakeRecorder headers in tests unless you explicitly need direct access to FakeRecorder APIs. + +## Typical test pattern: capture stdout and assert content + +```cpp +#include "score/mw/log/logging.h" + +#include +#include + +TEST(FakeRecorderUsage, CapturesLogOutput) +{ + testing::internal::CaptureStdout(); + + // Any mw::log statement will be routed to stdout when FakeRecorder is installed + score::mw::log::LogFatal("test") << "HELLO"; + + const std::string out = testing::internal::GetCapturedStdout(); + // Note: always call GetCapturedStdout() inside the test to finalize the capture. + EXPECT_THAT(out, ::testing::HasSubstr("HELLO")); +} +``` + +## What gets validated? + +The most reliable assertion is that the captured stdout **contains the text you streamed** into the logger (e.g. `"HELLO"`, `"EVENT"`, `"INVALID"`). + +Exact formatting of severity / context / metadata is not guaranteed by FakeRecorder and should not be asserted unless the logger itself guarantees it. + +## Thread-safety notes + +FakeRecorder serializes: + +* internal state updates using `Synchronized` +* stdout writes using a global mutex to avoid interleaved output + +When multiple threads log concurrently: + +* stdout writes are guarded to minimize interleaving +* ordering between threads may be nondeterministic (expected) + +## Real example in the repository + +`score/mw/com/impl/service_element_type_test.cpp` demonstrates the intended usage: + +* the test target depends on `auto_register_fake_recorder_env` +* the test captures stdout +* the test asserts that expected text appears in captured output diff --git a/score/mw/log/test/fake_recorder/fake_recorder.cpp b/score/mw/log/test/fake_recorder/fake_recorder.cpp new file mode 100644 index 0000000..e4f49ac --- /dev/null +++ b/score/mw/log/test/fake_recorder/fake_recorder.cpp @@ -0,0 +1,251 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include "score/mw/log/test/fake_recorder/fake_recorder.h" + +#include +#include +#include +#include +#include + +namespace score +{ +namespace mw +{ +namespace log +{ +namespace test +{ +namespace +{ +std::mutex g_stdout_mutex; + +/// Convert unsigned integer to binary string using std::bitset +template +std::string ToBinaryString(std::uint64_t value) +{ + return "0b" + std::bitset(value).to_string(); +} +} // namespace + +score::cpp::optional FakeRecorder::StartRecord(const std::string_view /*context_id*/, + const LogLevel /*log_level*/) noexcept +{ + return state_.WithLock([](State& s) -> score::cpp::optional { + for (std::size_t i = 0U; i < kMaxSlots; ++i) + { + if (!s.in_flight[i].has_value()) + { + s.in_flight[i].emplace(); + return SlotHandle(static_cast(i)); + } + } + return {}; + }); +} + +void FakeRecorder::StopRecord(const SlotHandle& slot) noexcept +{ + FlushSlot(slot); +} + +bool FakeRecorder::IsLogEnabled(const LogLevel& /*log_level*/, const std::string_view /*context*/) const noexcept +{ + return true; +} + +void FakeRecorder::Log(const SlotHandle& slot, const bool data) noexcept +{ + AppendToSlot(slot, data ? "true" : "false"); +} + +void FakeRecorder::Log(const SlotHandle& slot, const std::uint8_t data) noexcept +{ + AppendToSlot(slot, std::to_string(static_cast(data))); +} + +void FakeRecorder::Log(const SlotHandle& slot, const std::uint16_t data) noexcept +{ + AppendToSlot(slot, std::to_string(static_cast(data))); +} + +void FakeRecorder::Log(const SlotHandle& slot, const std::uint32_t data) noexcept +{ + AppendToSlot(slot, std::to_string(data)); +} + +void FakeRecorder::Log(const SlotHandle& slot, const std::uint64_t data) noexcept +{ + AppendToSlot(slot, std::to_string(data)); +} + +void FakeRecorder::Log(const SlotHandle& slot, const std::int8_t data) noexcept +{ + AppendToSlot(slot, std::to_string(static_cast(data))); +} + +void FakeRecorder::Log(const SlotHandle& slot, const std::int16_t data) noexcept +{ + AppendToSlot(slot, std::to_string(data)); +} + +void FakeRecorder::Log(const SlotHandle& slot, const std::int32_t data) noexcept +{ + AppendToSlot(slot, std::to_string(data)); +} + +void FakeRecorder::Log(const SlotHandle& slot, const std::int64_t data) noexcept +{ + AppendToSlot(slot, std::to_string(data)); +} + +void FakeRecorder::Log(const SlotHandle& slot, const float data) noexcept +{ + AppendToSlot(slot, std::to_string(data)); +} + +void FakeRecorder::Log(const SlotHandle& slot, const double data) noexcept +{ + AppendToSlot(slot, std::to_string(data)); +} + +void FakeRecorder::Log(const SlotHandle& slot, const std::string_view data) noexcept +{ + AppendToSlot(slot, data); +} + +void FakeRecorder::Log(const SlotHandle& slot, const LogRawBuffer data) noexcept +{ + const auto* begin = data.data(); + AppendToSlot(slot, std::string_view(begin, data.size())); +} + +void FakeRecorder::Log(const SlotHandle& slot, const LogSlog2Message /*data*/) noexcept +{ + AppendToSlot(slot, "[LogSlog2Message]"); +} + +void FakeRecorder::Log(const SlotHandle& slot, const LogHex8 data) noexcept +{ + char buf[8]; + std::snprintf(buf, sizeof(buf), "0x%02X", data.value); + AppendToSlot(slot, buf); +} + +void FakeRecorder::Log(const SlotHandle& slot, const LogHex16 data) noexcept +{ + char buf[16]; + std::snprintf(buf, sizeof(buf), "0x%04X", data.value); + AppendToSlot(slot, buf); +} + +void FakeRecorder::Log(const SlotHandle& slot, const LogHex32 data) noexcept +{ + char buf[16]; + std::snprintf(buf, sizeof(buf), "0x%08X", data.value); + AppendToSlot(slot, buf); +} + +void FakeRecorder::Log(const SlotHandle& slot, const LogHex64 data) noexcept +{ + char buf[24]; + std::snprintf(buf, sizeof(buf), "0x%016llX", static_cast(data.value)); + AppendToSlot(slot, buf); +} + +void FakeRecorder::Log(const SlotHandle& slot, const LogBin8 data) noexcept +{ + AppendToSlot(slot, ToBinaryString<8>(static_cast(data.value))); +} + +void FakeRecorder::Log(const SlotHandle& slot, const LogBin16 data) noexcept +{ + AppendToSlot(slot, ToBinaryString<16>(static_cast(data.value))); +} + +void FakeRecorder::Log(const SlotHandle& slot, const LogBin32 data) noexcept +{ + AppendToSlot(slot, ToBinaryString<32>(static_cast(data.value))); +} + +void FakeRecorder::Log(const SlotHandle& slot, const LogBin64 data) noexcept +{ + AppendToSlot(slot, ToBinaryString<64>(data.value)); +} + +std::vector FakeRecorder::GetRecordedMessages() const noexcept +{ + return state_.WithLock([](const State& s) { + return s.recorded_messages; + }); +} + +void FakeRecorder::ClearRecordedMessages() noexcept +{ + state_.WithLock([](State& s) { + s.recorded_messages.clear(); + for (auto& slot : s.in_flight) + { + slot.reset(); + } + }); +} + +void FakeRecorder::AppendToSlot(const SlotHandle& slot, const std::string_view text) noexcept +{ + const auto idx = static_cast(slot.GetSlotOfSelectedRecorder()); + state_.WithLock([idx, text](State& s) { + if (!s.in_flight[idx].has_value()) + { + return; + } + s.in_flight[idx]->append(text); + }); +} + +void FakeRecorder::FlushSlot(const SlotHandle& slot) noexcept +{ + const auto idx = static_cast(slot.GetSlotOfSelectedRecorder()); + std::string msg; + + state_.WithLock([&msg, idx](State& s) { + if (!s.in_flight[idx].has_value()) + { + return; + } + + msg = std::move(*s.in_flight[idx]); + s.in_flight[idx].reset(); + }); + + if (msg.empty()) + { + return; + } + + { + std::lock_guard out_lock(g_stdout_mutex); + std::fwrite(msg.c_str(), 1U, msg.size(), stdout); + std::fwrite("\n", 1U, 1U, stdout); + std::fflush(stdout); + } + + state_.WithLock([&msg](State& s) { + s.recorded_messages.push_back(std::move(msg)); + }); +} + +} // namespace test +} // namespace log +} // namespace mw +} // namespace score diff --git a/score/mw/log/test/fake_recorder/fake_recorder.h b/score/mw/log/test/fake_recorder/fake_recorder.h new file mode 100644 index 0000000..8732805 --- /dev/null +++ b/score/mw/log/test/fake_recorder/fake_recorder.h @@ -0,0 +1,99 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#ifndef SCORE_MW_LOG_TEST_FAKE_RECORDER_FAKE_RECORDER_H +#define SCORE_MW_LOG_TEST_FAKE_RECORDER_FAKE_RECORDER_H + +#include "score/mw/log/recorder.h" +#include "score/datarouter/lib/synchronized/synchronized.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace score +{ +namespace mw +{ +namespace log +{ +namespace test +{ + +class FakeRecorder final : public score::mw::log::Recorder +{ + public: + FakeRecorder() = default; + ~FakeRecorder() noexcept override = default; + + FakeRecorder(const FakeRecorder&) = delete; + FakeRecorder& operator=(const FakeRecorder&) = delete; + FakeRecorder(FakeRecorder&&) = delete; + FakeRecorder& operator=(FakeRecorder&&) = delete; + + // Recorder interface implementation + score::cpp::optional StartRecord(std::string_view context_id, LogLevel log_level) noexcept override; + void StopRecord(const SlotHandle& slot) noexcept override; + bool IsLogEnabled(const LogLevel& log_level, const std::string_view context) const noexcept override; + + void Log(const SlotHandle& slot, bool data) noexcept override; + void Log(const SlotHandle& slot, std::uint8_t data) noexcept override; + void Log(const SlotHandle& slot, std::uint16_t data) noexcept override; + void Log(const SlotHandle& slot, std::uint32_t data) noexcept override; + void Log(const SlotHandle& slot, std::uint64_t data) noexcept override; + void Log(const SlotHandle& slot, std::int8_t data) noexcept override; + void Log(const SlotHandle& slot, std::int16_t data) noexcept override; + void Log(const SlotHandle& slot, std::int32_t data) noexcept override; + void Log(const SlotHandle& slot, std::int64_t data) noexcept override; + void Log(const SlotHandle& slot, float data) noexcept override; + void Log(const SlotHandle& slot, double data) noexcept override; + void Log(const SlotHandle& slot, std::string_view data) noexcept override; + void Log(const SlotHandle& slot, LogHex8 data) noexcept override; + void Log(const SlotHandle& slot, LogHex16 data) noexcept override; + void Log(const SlotHandle& slot, LogHex32 data) noexcept override; + void Log(const SlotHandle& slot, LogHex64 data) noexcept override; + void Log(const SlotHandle& slot, LogBin8 data) noexcept override; + void Log(const SlotHandle& slot, LogBin16 data) noexcept override; + void Log(const SlotHandle& slot, LogBin32 data) noexcept override; + void Log(const SlotHandle& slot, LogBin64 data) noexcept override; + void Log(const SlotHandle& slot, const LogRawBuffer data) noexcept override; + void Log(const SlotHandle& slot, const LogSlog2Message data) noexcept override; + + std::vector GetRecordedMessages() const noexcept; + + void ClearRecordedMessages() noexcept; + + private: + void AppendToSlot(const SlotHandle& slot, std::string_view text) noexcept; + void FlushSlot(const SlotHandle& slot) noexcept; + + static constexpr std::size_t kMaxSlots{256U}; + + struct State + { + std::array, kMaxSlots> in_flight{}; + std::vector recorded_messages{}; + }; + + score::platform::datarouter::Synchronized state_{}; +}; + +} // namespace test +} // namespace log +} // namespace mw +} // namespace score + +#endif // SCORE_MW_LOG_TEST_FAKE_RECORDER_FAKE_RECORDER_H diff --git a/score/mw/log/test/fake_recorder_environment/BUILD b/score/mw/log/test/fake_recorder_environment/BUILD new file mode 100644 index 0000000..24b4d09 --- /dev/null +++ b/score/mw/log/test/fake_recorder_environment/BUILD @@ -0,0 +1,41 @@ +# ******************************************************************************* +# Copyright (c) 2025 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") + +cc_library( + name = "fake_recorder_environment", + testonly = True, + srcs = ["fake_recorder_environment.cpp"], + hdrs = ["fake_recorder_environment.h"], + features = COMPILER_WARNING_FEATURES, + visibility = ["//visibility:public"], + deps = [ + "//score/mw/log/test/fake_recorder", + "@googletest//:gtest", + "@score_baselibs//score/mw/log:frontend", + ], +) + +cc_library( + name = "auto_register_fake_recorder_env", + testonly = True, + srcs = ["register_fake_recorder_environment.cpp"], + features = COMPILER_WARNING_FEATURES, + visibility = ["//visibility:public"], + deps = [ + ":fake_recorder_environment", + "@googletest//:gtest", + ], + alwayslink = True, # Ensure static initializer runs +) diff --git a/score/mw/log/test/fake_recorder_environment/fake_recorder_environment.cpp b/score/mw/log/test/fake_recorder_environment/fake_recorder_environment.cpp new file mode 100644 index 0000000..6a9f170 --- /dev/null +++ b/score/mw/log/test/fake_recorder_environment/fake_recorder_environment.cpp @@ -0,0 +1,47 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include "score/mw/log/test/fake_recorder_environment/fake_recorder_environment.h" + +#include "score/mw/log/runtime.h" + +namespace score +{ +namespace mw +{ +namespace log +{ +namespace test +{ + +void FakeRecorderEnvironment::SetUp() +{ + // Save current recorder (if any) before installing fake + previous_recorder_ = &score::mw::log::detail::Runtime::GetRecorder(); + + // Create and install fake recorder + recorder_ = std::make_unique(); + score::mw::log::detail::Runtime::SetRecorder(recorder_.get()); +} + +void FakeRecorderEnvironment::TearDown() +{ + // Restore previous recorder (or nullptr) + score::mw::log::detail::Runtime::SetRecorder(previous_recorder_); + recorder_.reset(); + previous_recorder_ = nullptr; +} + +} // namespace test +} // namespace log +} // namespace mw +} // namespace score diff --git a/score/mw/log/test/fake_recorder_environment/fake_recorder_environment.h b/score/mw/log/test/fake_recorder_environment/fake_recorder_environment.h new file mode 100644 index 0000000..94a0774 --- /dev/null +++ b/score/mw/log/test/fake_recorder_environment/fake_recorder_environment.h @@ -0,0 +1,55 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#ifndef SCORE_MW_LOG_TEST_FAKE_RECORDER_ENVIRONMENT_FAKE_RECORDER_ENVIRONMENT_H +#define SCORE_MW_LOG_TEST_FAKE_RECORDER_ENVIRONMENT_FAKE_RECORDER_ENVIRONMENT_H + +#include "score/mw/log/test/fake_recorder/fake_recorder.h" + +#include + +#include + +namespace score +{ +namespace mw +{ +namespace log +{ +namespace test +{ + +class FakeRecorderEnvironment : public ::testing::Environment +{ + public: + FakeRecorderEnvironment() noexcept = default; + ~FakeRecorderEnvironment() noexcept override = default; + + FakeRecorderEnvironment(const FakeRecorderEnvironment&) = delete; + FakeRecorderEnvironment& operator=(const FakeRecorderEnvironment&) = delete; + FakeRecorderEnvironment(FakeRecorderEnvironment&&) = delete; + FakeRecorderEnvironment& operator=(FakeRecorderEnvironment&&) = delete; + + void SetUp() override; + void TearDown() override; + + private: + std::unique_ptr recorder_; + score::mw::log::Recorder* previous_recorder_{nullptr}; +}; + +} // namespace test +} // namespace log +} // namespace mw +} // namespace score + +#endif // SCORE_MW_LOG_TEST_FAKE_RECORDER_ENVIRONMENT_FAKE_RECORDER_ENVIRONMENT_H diff --git a/score/mw/log/test/fake_recorder_environment/register_fake_recorder_environment.cpp b/score/mw/log/test/fake_recorder_environment/register_fake_recorder_environment.cpp new file mode 100644 index 0000000..64a668f --- /dev/null +++ b/score/mw/log/test/fake_recorder_environment/register_fake_recorder_environment.cpp @@ -0,0 +1,30 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include "score/mw/log/test/fake_recorder_environment/fake_recorder_environment.h" + +#include + +namespace +{ + +struct AutoRegisterFakeRecorderEnvironment +{ + AutoRegisterFakeRecorderEnvironment() + { + ::testing::AddGlobalTestEnvironment(new score::mw::log::test::FakeRecorderEnvironment()); + } +}; + +static AutoRegisterFakeRecorderEnvironment auto_register_instance; + +} // namespace