From 705f8711fa130b167f850780f4c4d51587fec171 Mon Sep 17 00:00:00 2001 From: Patil Date: Mon, 22 Dec 2025 15:11:00 +0530 Subject: [PATCH 01/34] Fix for score_docs_as_code version, .bazelrc --- .bazelrc | 2 +- MODULE.bazel | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.bazelrc b/.bazelrc index af339ec..ff537e8 100644 --- a/.bazelrc +++ b/.bazelrc @@ -12,7 +12,7 @@ # ******************************************************************************* common --@score_baselibs//score/memory/shared/flags:use_typedshmd=False -common --@score-baselibs//score/json:base_library=nlohmann +common --@score_baselibs//score/json:base_library=nlohmann common --//score/datarouter/build_configuration_flags:persistent_logging=False diff --git a/MODULE.bazel b/MODULE.bazel index d8ba2aa..6012271 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -25,6 +25,7 @@ bazel_dep(name = "rules_rust", version = "0.63.0") bazel_dep(name = "rapidjson", version = "1.1.0") bazel_dep(name = "score_crates", version = "0.0.3", repo_name = "crate_index") bazel_dep(name = "jemalloc", version = "5.3.0-bcr.alpha.4") + bazel_dep(name = "libatomic", version = "1.0") local_path_override( module_name = "libatomic", @@ -160,4 +161,4 @@ bazel_dep(name = "aspect_rules_lint", version = "1.5.3") bazel_dep(name = "buildifier_prebuilt", version = "8.2.0.2") #docs-as-code -bazel_dep(name = "score_docs_as_code", version = "1.1.0") +bazel_dep(name = "score_docs_as_code", version = "2.0.2") From a06cf89bc00d4136b7d457fe66c9f610525810d0 Mon Sep 17 00:00:00 2001 From: Patil Date: Mon, 22 Dec 2025 15:34:28 +0530 Subject: [PATCH 02/34] Remove local overridden libatomic dep as not required --- MODULE.bazel | 6 ------ 1 file changed, 6 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index 6012271..c2d7d07 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -26,12 +26,6 @@ bazel_dep(name = "rapidjson", version = "1.1.0") bazel_dep(name = "score_crates", version = "0.0.3", repo_name = "crate_index") bazel_dep(name = "jemalloc", version = "5.3.0-bcr.alpha.4") -bazel_dep(name = "libatomic", version = "1.0") -local_path_override( - module_name = "libatomic", - path = "third_party/libatomic", -) - gcc = use_extension("@score_toolchains_gcc//extentions:gcc.bzl", "gcc", dev_dependency = True) gcc.toolchain( sha256 = "457f5f20f57528033cb840d708b507050d711ae93e009388847e113b11bf3600", From b24b911bb870d8730b998d7be02093d988b67378 Mon Sep 17 00:00:00 2001 From: BMW Engineer Date: Mon, 19 Jan 2026 18:43:05 +0100 Subject: [PATCH 03/34] Project import generated by Copybara. GIT_ORIGIN_SPP_REV_ID: 8a2b2eea1f568d6202ab434296573a5e11e2757a --- score/datarouter/BUILD | 75 ++- score/datarouter/README.md | 4 +- score/datarouter/datarouter/data_router.cpp | 61 +- score/datarouter/datarouter/data_router.h | 39 +- .../applications/datarouter_feature_config.h | 4 + .../include/daemon/dlt_log_server.h | 4 +- .../include/daemon/message_passing_server.h | 2 +- .../datarouter/include/daemon/socketserver.h | 61 ++ score/datarouter/include/dlt/dlt_common.h | 40 -- score/datarouter/include/dlt/dlt_protocol.h | 40 -- score/datarouter/include/dlt/dlt_types.h | 40 -- .../include/logparser/i_logparser.h | 105 ++++ .../datarouter/include/logparser/logparser.h | 68 +-- .../include/unix_domain/unix_domain_server.h | 2 +- score/datarouter/mocks/logparser_mock.h | 47 ++ .../nonverbose_dlt_stub/stub_nonverbose_dlt.h | 4 +- .../src/applications/main_nonadaptive.cpp | 2 +- .../src/daemon/message_passing_server.cpp | 26 +- .../src/daemon/persistentlogging_config.cpp | 3 +- score/datarouter/src/daemon/socketserver.cpp | 306 +++++++--- .../src/daemon/socketserver_config.cpp | 6 +- .../src/daemon/udp_stream_output.cpp | 60 +- score/datarouter/src/logparser/logparser.cpp | 8 + .../src/unix_domain/unix_domain_server.cpp | 13 +- score/datarouter/test/ut/ut_logging/BUILD | 21 + .../test/ut/ut_logging/test_socketserver.cpp | 568 ++++++++++++++++++ .../detail/data_router/shared_memory/BUILD | 11 + .../detail/data_router/shared_memory/common.h | 6 +- .../shared_memory/i_shared_memory_reader.h | 78 +++ .../shared_memory/reader_factory.h | 9 +- .../shared_memory/reader_factory_impl.cpp | 19 +- .../shared_memory/reader_factory_impl.h | 10 +- .../shared_memory/reader_factory_mock.h | 8 +- .../shared_memory/reader_factory_test.cpp | 16 +- .../shared_memory/shared_memory_reader.h | 61 +- .../shared_memory/shared_memory_reader_mock.h | 55 ++ .../shared_memory/shared_memory_writer.h | 6 +- .../shared_memory/writer_factory.h | 4 +- 38 files changed, 1453 insertions(+), 439 deletions(-) create mode 100644 score/datarouter/include/logparser/i_logparser.h create mode 100644 score/datarouter/mocks/logparser_mock.h create mode 100644 score/datarouter/test/ut/ut_logging/test_socketserver.cpp create mode 100644 score/mw/log/detail/data_router/shared_memory/i_shared_memory_reader.h create mode 100644 score/mw/log/detail/data_router/shared_memory/shared_memory_reader_mock.h diff --git a/score/datarouter/BUILD b/score/datarouter/BUILD index 6f30d31..d0ed939 100644 --- a/score/datarouter/BUILD +++ b/score/datarouter/BUILD @@ -153,6 +153,26 @@ cc_library( ## Log parser ## --------------------------------------------------------------------------- +cc_library( + name = "logparser_interface", + hdrs = [ + "include/logparser/i_logparser.h", + ], + 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/test:__subpackages__", + ], + deps = [ + "//score/mw/log/detail/data_router/shared_memory:reader", + ], +) + cc_library( name = "logparser", srcs = [ @@ -174,6 +194,7 @@ cc_library( ":datarouter_types", ":dltprotocol", ":log", + ":logparser_interface", "//score/mw/log/detail/data_router/shared_memory:reader", "@score_baselibs//score/static_reflection_with_serialization/serialization", ], @@ -191,12 +212,27 @@ cc_library( deps = [ ":datarouter_types", ":dltprotocol", + ":logparser_interface", "//score/mw/log/detail/data_router/shared_memory:reader", "@score_baselibs//score/mw/log/configuration:nvconfig", "@score_baselibs//score/static_reflection_with_serialization/serialization", ], ) +cc_library( + name = "logparser_mock", + hdrs = [ + "mocks/logparser_mock.h", + ], + features = COMPILER_WARNING_FEATURES, + strip_include_prefix = "mocks", + visibility = ["//score/datarouter/test:__subpackages__"], + deps = [ + ":logparser_interface", + "@googletest//:gtest_main", + ], +) + ## =========================================================================== ## libtracing ## --------------------------------------------------------------------------- @@ -664,6 +700,7 @@ cc_library( strip_include_prefix = "include", visibility = ["//visibility:private"], deps = [ + ":datarouter_feature_config", ":datarouter_lib", ":dltserver_common", ":persistentlogconfig", @@ -693,6 +730,7 @@ cc_library( strip_include_prefix = "include", visibility = ["//score/datarouter/test:__subpackages__"], deps = [ + ":datarouter_feature_config", ":datarouter_testing", ":dltserver_common", ":persistentlogconfig", @@ -737,6 +775,38 @@ cc_library( }), ) +cc_library( + name = "datarouter_socketserver_testing", + testonly = True, + srcs = [ + "src/daemon/socketserver.cpp", + ], + hdrs = [ + "include/daemon/socketserver.h", + ], + # TODO: will be reworked in Ticket-207823 + features = COMPILER_WARNING_FEATURES, + local_defines = select({ + "//score/datarouter/build_configuration_flags:config_persistent_logging": ["PERSISTENT_LOGGING"], + "//conditions:default": [], + }), + strip_include_prefix = "include", + visibility = ["//score/datarouter/test:__subpackages__"], + deps = [ + ":datarouter_feature_config", + ":datarouter_lib", + ":dltserver", + ":persistentlogconfig", + ":socketserver_config_lib_testing", + "@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", + ], + "//conditions:default": [], + }), +) + cc_library( name = "datarouter_app", srcs = [ @@ -762,10 +832,7 @@ cc_binary( ], features = COMPILER_WARNING_FEATURES, visibility = [ - "//ecu/xpad/xpad-shared/config/common/pas/datarouter:__subpackages__", - "//platform/aas/tools/itf:__subpackages__", - "//platform/aas/tools/sctf:__subpackages__", - # "@ddad//ecu/xpad/xpad-shared/config/common/pas/datarouter:__subpackages__", + "//visibility:public", ], deps = [ ":datarouter_app", diff --git a/score/datarouter/README.md b/score/datarouter/README.md index 4220a7f..dc07ea1 100644 --- a/score/datarouter/README.md +++ b/score/datarouter/README.md @@ -32,13 +32,13 @@ Example statistics message: ``` [APP1 : count 4074 , size 714378 B, read_time: 21000 us, transp_delay: 204000 us time_between_to_calls_: 0 us time_to_process_records_: 0 us buffer size watermark: 152 KB out of 512 KB ( 29 %) messages dropped: 0 (accumulated)] ``` -This example shows source `APP1` transmitted 4074 messages with a total payload size of 714378 bytes. The datarouter spent 204000 microseconds reading the messages (since the last read cycle), with a maximum message latency of 1364 microseconds. +This example shows source `APP1` transmitted 4074 messages with a total payload size of 714378 bytes. The datarouter spent 204000 microseconds reading the messages (since the last read cycle). The datarouter detects message gaps in the incoming flow and reports them using the source ID as the context ID. Example gap detection message: ``` -535575 2019/11/05 14:59:22.720133 244.3038 157 HFPP DR 485 0 log error verbose 5 message drop detected: messages 1073 to 1110 lost! +535575 2019/11/05 14:59:22.720133 244.3038 157 ECU1 DR 485 0 log error verbose 5 message drop detected: messages 1073 to 1110 lost! ``` The source ID corresponds to its PID. In this example, the source with PID `485` lost 37 messages because the datarouter did not read the ring buffer fast enough. diff --git a/score/datarouter/datarouter/data_router.cpp b/score/datarouter/datarouter/data_router.cpp index 5491baa..a64b307 100644 --- a/score/datarouter/datarouter/data_router.cpp +++ b/score/datarouter/datarouter/data_router.cpp @@ -93,14 +93,14 @@ DataRouter::MessagingSessionPtr DataRouter::new_source_session( // It shall be safe to create shared memory reader as only single process - Datarouter daemon, shall be running // at all times in whole system. auto reader = reader_factory->Create(fd, client_pid); - if (reader.has_value() == false) + if (reader == nullptr) { stats_logger_.LogError() << "Failed to create session for pid=" << client_pid << ", appid=" << name; return nullptr; } return new_source_session_impl( - name, is_dlt_enabled, std::move(handle), quota, quota_enforcement_enabled, std::move(reader.value()), nvConfig); + name, is_dlt_enabled, std::move(handle), quota, quota_enforcement_enabled, std::move(reader), nvConfig); } void DataRouter::show_source_statistics(uint16_t series_num) @@ -119,20 +119,21 @@ std::unique_ptr DataRouter::new_source_session_impl( SessionHandleVariant handle, const double quota, bool quota_enforcement_enabled, - score::mw::log::detail::SharedMemoryReader reader, + 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), - name, - is_dlt_enabled, - std::move(handle), - quota, - quota_enforcement_enabled, - stats_logger_, - nvConfig); + auto sourceSession = + std::make_unique(*this, + std::move(reader), + name, + is_dlt_enabled, + std::move(handle), + quota, + quota_enforcement_enabled, + stats_logger_, + std::make_unique(nvConfig)); if (sourceCallback_) { sourceCallback_(std::move(sourceSession->get_parser())); @@ -178,9 +179,9 @@ bool DataRouter::SourceSession::tryFinalizeAcquisition(bool& needs_fast_reschedu if (data_acquired_local.has_value()) { - if (reader_.IsBlockReleasedByWriters(data_acquired_local.value().acquired_buffer)) + if (reader_->IsBlockReleasedByWriters(data_acquired_local.value().acquired_buffer)) { - std::ignore = reader_.NotifyAcquisitionSetReader(data_acquired_local.value()); + std::ignore = reader_->NotifyAcquisitionSetReader(data_acquired_local.value()); command_data_.lock()->data_acquired_ = std::nullopt; return true; @@ -213,7 +214,7 @@ void DataRouter::SourceSession::processAndRouteLogMessages(uint64_t& message_cou score::mw::log::detail::TypeRegistrationCallback on_new_type = [this](const score::mw::log::detail::TypeRegistration& registration) noexcept { - parser_.AddIncomingType(registration); + parser_->AddIncomingType(registration); }; score::mw::log::detail::NewRecordCallback on_new_record = @@ -225,7 +226,7 @@ void DataRouter::SourceSession::processAndRouteLogMessages(uint64_t& message_cou } auto record_received_timestamp = score::mw::log::detail::TimePoint::clock::now(); - parser_.Parse(record); + parser_->Parse(record); ++message_count_local; transport_delay_local = std::max(transport_delay_local, @@ -234,7 +235,7 @@ void DataRouter::SourceSession::processAndRouteLogMessages(uint64_t& message_cou }; bool detach_needed = false; - const auto number_of_bytes_in_buffer_result = reader_.Read(on_new_type, on_new_record); + const auto number_of_bytes_in_buffer_result = reader_->Read(on_new_type, on_new_record); if (number_of_bytes_in_buffer_result.has_value()) { number_of_bytes_in_buffer = number_of_bytes_in_buffer_result.value(); @@ -262,7 +263,7 @@ void DataRouter::SourceSession::processAndRouteLogMessages(uint64_t& message_cou if (cmd->block_expected_to_be_next.has_value()) { const auto peek_bytes = - reader_.PeekNumberOfBytesAcquiredInBuffer(cmd->block_expected_to_be_next.value()); + reader_->PeekNumberOfBytesAcquiredInBuffer(cmd->block_expected_to_be_next.value()); if ((peek_bytes.has_value() && peek_bytes.value() > 0) || (cmd->ticks_without_write > kTicksWithoutAcquireWhileNoWrites)) @@ -291,12 +292,12 @@ void DataRouter::SourceSession::processAndRouteLogMessages(uint64_t& message_cou void DataRouter::SourceSession::process_detached_logs(uint64_t& number_of_bytes_in_buffer) { - const auto number_of_bytes_in_buffer_result_detached = reader_.ReadDetached( + const auto number_of_bytes_in_buffer_result_detached = reader_->ReadDetached( [this](const auto& registration) noexcept { - parser_.AddIncomingType(registration); + parser_->AddIncomingType(registration); }, [this](const auto& record) noexcept { - parser_.Parse(record); + parser_->Parse(record); }); if (number_of_bytes_in_buffer_result_detached.has_value()) @@ -316,8 +317,8 @@ void DataRouter::SourceSession::update_and_log_stats(uint64_t message_count_loca { auto stats = stats_data_.lock(); - const auto message_count_dropped_new = reader_.GetNumberOfDropsWithBufferFull(); - const auto size_dropped_new = reader_.GetSizeOfDropsWithBufferFull(); + const auto message_count_dropped_new = reader_->GetNumberOfDropsWithBufferFull(); + const auto size_dropped_new = reader_->GetSizeOfDropsWithBufferFull(); if (message_count_dropped_new != stats->message_count_dropped) { stats_logger_.LogError() << stats->name << ": message drop detected: " @@ -327,7 +328,7 @@ void DataRouter::SourceSession::update_and_log_stats(uint64_t message_count_loca stats->size_dropped = size_dropped_new; } - const auto message_count_dropped_invalid_size_new = reader_.GetNumberOfDropsWithInvalidSize(); + const auto message_count_dropped_invalid_size_new = reader_->GetNumberOfDropsWithInvalidSize(); if (message_count_dropped_invalid_size_new != stats->message_count_dropped_invalid_size) { stats_logger_.LogError() << stats->name << ": message drop detected: " @@ -349,14 +350,14 @@ void DataRouter::SourceSession::update_and_log_stats(uint64_t message_count_loca } DataRouter::SourceSession::SourceSession(DataRouter& router, - score::mw::log::detail::SharedMemoryReader reader, + std::unique_ptr reader, const std::string& name, const bool is_dlt_enabled, SessionHandleVariant handle, const double quota, bool quota_enforcement_enabled, score::mw::log::Logger& stats_logger, - const score::mw::log::NvConfig& nvConfig) + std::unique_ptr parser) : UnixDomainServer::ISession{}, MessagePassingServer::ISession{}, local_subscriber_data_(LocalSubscriberData{}), @@ -364,7 +365,7 @@ DataRouter::SourceSession::SourceSession(DataRouter& router, stats_data_(StatsData{}), router_(router), reader_(std::move(reader)), - parser_(nvConfig), + parser_(std::move(parser)), handle_(std::move(handle)), stats_logger_(stats_logger) { @@ -464,7 +465,7 @@ void DataRouter::SourceSession::show_stats() name = stats->name; } - const auto buffer_size_kb = reader_.GetRingBufferSizeBytes() / 1024U / 2U; + const auto buffer_size_kb = reader_->GetRingBufferSizeBytes() / 1024U / 2U; auto buffer_watermark_kb = max_bytes_in_buffer / 1024U; if (message_count_dropped > 0) @@ -533,7 +534,8 @@ void DataRouter::SourceSession::request_acquire() }, [](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_); } @@ -548,7 +550,6 @@ void DataRouter::SourceSession::on_closed_by_peer() { command_data_.lock()->command_detach_on_closed = true; } -// LCOV_EXCL_STOP } // namespace datarouter } // namespace platform diff --git a/score/datarouter/datarouter/data_router.h b/score/datarouter/datarouter/data_router.h index e09d643..27d2926 100644 --- a/score/datarouter/datarouter/data_router.h +++ b/score/datarouter/datarouter/data_router.h @@ -39,7 +39,7 @@ namespace platform namespace datarouter { -using internal::LogParser; +using internal::ILogParser; using internal::MessagePassingServer; using internal::UnixDomainServer; @@ -84,7 +84,7 @@ struct StatsData class DataRouter { public: - using SourceSetupCallback = std::function; + using SourceSetupCallback = std::function; using SessionPtr = std::unique_ptr; using MessagingSessionPtr = std::unique_ptr; @@ -120,6 +120,9 @@ class DataRouter void show_source_statistics(uint16_t series_num); + // for unit test only. to keep rest of functions in private + class DataRouterForTest; + private: /** * class SourceSession is private to Datarouter and could not be used outside Datarouter, so it is safe to keep in @@ -127,18 +130,18 @@ class DataRouter * MessagePassingServer::ISession / UnixDomainServer::ISession */ // NOLINTNEXTLINE(fuchsia-multiple-inheritance) - Both base classes are pure interfaces - class SourceSession final : public UnixDomainServer::ISession, public MessagePassingServer::ISession + class SourceSession : public UnixDomainServer::ISession, public MessagePassingServer::ISession { public: SourceSession(DataRouter& router, - score::mw::log::detail::SharedMemoryReader reader, + std::unique_ptr reader, const std::string& name, const bool is_dlt_enabled, SessionHandleVariant handle, const double quota, bool quota_enforcement_enabled, score::mw::log::Logger& stats_logger, - const score::mw::log::NvConfig& nvConfig); + std::unique_ptr parser); SourceSession(const SourceSession&) = delete; SourceSession& operator=(const SourceSession&) = delete; @@ -151,6 +154,9 @@ class DataRouter } ~SourceSession(); + // for unit test only. to keep rest of functions in private + class SourceSessionForTest; + private: bool is_source_closed() override { @@ -183,26 +189,27 @@ class DataRouter Synchronized stats_data_; DataRouter& router_; - score::mw::log::detail::SharedMemoryReader reader_; - LogParser parser_; + std::unique_ptr reader_; + std::unique_ptr parser_; SessionHandleVariant handle_; score::mw::log::Logger& stats_logger_; public: void show_stats(); - LogParser& get_parser() + ILogParser& get_parser() { - return parser_; + return *(parser_.get()); } }; - std::unique_ptr new_source_session_impl(const std::string name, - const bool is_dlt_enabled, - SessionHandleVariant handle, - const double quota, - bool quota_enforcement_enabled, - score::mw::log::detail::SharedMemoryReader reader, - const score::mw::log::NvConfig& nvConfig); + std::unique_ptr new_source_session_impl( + const std::string name, + const bool is_dlt_enabled, + SessionHandleVariant handle, + const double quota, + bool quota_enforcement_enabled, + std::unique_ptr reader, + const score::mw::log::NvConfig& nvConfig); score::mw::log::Logger& stats_logger_; diff --git a/score/datarouter/include/applications/datarouter_feature_config.h b/score/datarouter/include/applications/datarouter_feature_config.h index 5cf0375..586b881 100644 --- a/score/datarouter/include/applications/datarouter_feature_config.h +++ b/score/datarouter/include/applications/datarouter_feature_config.h @@ -72,14 +72,18 @@ namespace datarouter // --- Conditional Compilation Feature Switch --- #if defined(PERSISTENT_CONFIG_FEATURE_ENABLED) using PersistentDictionaryFactoryType = AraPerPersistentDictionaryFactory; +inline constexpr bool kPersistentConfigFeatureEnabled = true; #else using PersistentDictionaryFactoryType = StubPersistentDictionaryFactory; +inline constexpr bool kPersistentConfigFeatureEnabled = false; #endif #if defined(NON_VERBOSE_DLT) using DltNonverboseHandlerType = score::logging::dltserver::DltNonverboseHandler; +inline constexpr bool kNonVerboseDltEnabled = true; #else using DltNonverboseHandlerType = score::logging::dltserver::StubDltNonverboseHandler; +inline constexpr bool kNonVerboseDltEnabled = false; #endif #if defined(DYNAMIC_CONFIGURATION_IN_DATAROUTER) diff --git a/score/datarouter/include/daemon/dlt_log_server.h b/score/datarouter/include/daemon/dlt_log_server.h index cfd0538..4da19b5 100644 --- a/score/datarouter/include/daemon/dlt_log_server.h +++ b/score/datarouter/include/daemon/dlt_log_server.h @@ -100,7 +100,7 @@ class DltLogServer : score::platform::datarouter::DltNonverboseHandlerType::IOut virtual ~DltLogServer() = default; // Not possible to mock LogParser currently. // LCOV_EXCL_START - void add_handlers(LogParser& parser) + void add_handlers(ILogParser& parser) { parser.add_global_handler(*sysedr_handler_); parser.add_type_handler(PERSISTENT_REQUEST_TYPE_NAME, *sysedr_handler_); @@ -114,7 +114,7 @@ class DltLogServer : score::platform::datarouter::DltNonverboseHandlerType::IOut } } - void update_handlers(LogParser& parser, bool enabled) + void update_handlers(ILogParser& parser, bool enabled) { // protected by external mutex if (enabled) diff --git a/score/datarouter/include/daemon/message_passing_server.h b/score/datarouter/include/daemon/message_passing_server.h index 94eb688..28ee774 100644 --- a/score/datarouter/include/daemon/message_passing_server.h +++ b/score/datarouter/include/daemon/message_passing_server.h @@ -84,7 +84,7 @@ class MessagePassingServer : public IMessagePassingServerSessionWrapper score::cpp::pmr::unique_ptr)>; MessagePassingServer(SessionFactory factory, ::score::concurrency::Executor& executor); - ~MessagePassingServer(); + ~MessagePassingServer() noexcept; // for unit test only. to keep rest of functions in private class MessagePassingServerForTest; diff --git a/score/datarouter/include/daemon/socketserver.h b/score/datarouter/include/daemon/socketserver.h index d2216d6..e6040e6 100644 --- a/score/datarouter/include/daemon/socketserver.h +++ b/score/datarouter/include/daemon/socketserver.h @@ -14,8 +14,19 @@ #ifndef LOGGING_SOCKETSERVER_H #define LOGGING_SOCKETSERVER_H +#include "daemon/dlt_log_server.h" +#include "daemon/message_passing_server.h" +#include "logparser/logparser.h" +#include "score/mw/log/configuration/nvconfig.h" +#include "score/mw/log/logging.h" +#include "score/datarouter/datarouter/data_router.h" +#include "score/datarouter/src/persistency/i_persistent_dictionary.h" +#include "unix_domain/unix_domain_server.h" + #include +#include #include +#include namespace score { @@ -27,12 +38,62 @@ namespace datarouter class SocketServer { public: + struct PersistentStorageHandlers + { + std::function load_dlt; + std::function store_dlt; + bool is_dlt_enabled; + }; + static void run(const std::atomic_bool& exit_requested, const bool no_adaptive_runtime) { static SocketServer server; server.doWork(exit_requested, no_adaptive_runtime); } + static PersistentStorageHandlers InitializePersistentStorage( + std::unique_ptr& persistent_dictionary); + + static std::unique_ptr CreateDltServer( + const PersistentStorageHandlers& storage_handlers); + + static DataRouter::SourceSetupCallback CreateSourceSetupHandler(score::logging::dltserver::DltLogServer& dlt_server); + + // Static helper functions for testing lambda bodies + static void UpdateParserHandlers(score::logging::dltserver::DltLogServer& dlt_server, + score::platform::internal::ILogParser& parser, + bool enable); + + static void UpdateHandlersFinal(score::logging::dltserver::DltLogServer& dlt_server, bool enable); + + static std::unique_ptr CreateConfigSession( + score::logging::dltserver::DltLogServer& dlt_server, + score::platform::internal::UnixDomainServer::SessionHandle handle); + + static std::function CreateEnableHandler(DataRouter& router, + IPersistentDictionary& persistent_dictionary, + score::logging::dltserver::DltLogServer& dlt_server); + + static std::unique_ptr CreateUnixDomainServer( + score::logging::dltserver::DltLogServer& dlt_server); + + static std::unique_ptr CreateMessagePassingSession( + DataRouter& router, + score::logging::dltserver::DltLogServer& dlt_server, + const score::mw::log::NvConfig& nv_config, + const pid_t client_pid, + const score::mw::log::detail::ConnectMessageFromClient& conn, + score::cpp::pmr::unique_ptr handle); + + static score::mw::log::NvConfig LoadNvConfig( + score::mw::log::Logger& stats_logger, + const std::string& config_path = "/bmw/platform/opt/datarouter/etc/class-id.json"); + + static void RunEventLoop(const std::atomic_bool& exit_requested, + DataRouter& router, + score::logging::dltserver::DltLogServer& dlt_server, + score::mw::log::Logger& stats_logger); + private: void doWork(const std::atomic_bool& exit_requested, const bool no_adaptive_runtime); }; diff --git a/score/datarouter/include/dlt/dlt_common.h b/score/datarouter/include/dlt/dlt_common.h index 23bc81d..93a67ee 100644 --- a/score/datarouter/include/dlt/dlt_common.h +++ b/score/datarouter/include/dlt/dlt_common.h @@ -15,46 +15,6 @@ * @licence end@ */ -/*! - * \author Alexander Wenzel - * - * \copyright Copyright © 2011-2015 BMW AG. \n - * License MPL-2.0: Mozilla Public License version 2.0 http://mozilla.org/MPL/2.0/. - * - * \file dlt_common.h - */ - -/******************************************************************************* -** ** -** SRC-MODULE: dlt_common.h ** -** ** -** TARGET : linux ** -** ** -** PROJECT : DLT ** -** ** -** AUTHOR : Alexander Wenzel ** -** Markus Klein ** -** ** -** PURPOSE : ** -** ** -** REMARKS : ** -** ** -** PLATFORM DEPENDANT [yes/no]: yes ** -** ** -** TO BE CHANGED BY USER [yes/no]: no ** -** ** -*******************************************************************************/ - -/******************************************************************************* -** Author Identity ** -******************************************************************************** -** ** -** Initials Name Company ** -** -------- ------------------------- ---------------------------------- ** -** aw Alexander Wenzel BMW ** -** mk Markus Klein Fraunhofer ESK ** -*******************************************************************************/ - /******************************************************************************* ** Revision Control History ** *******************************************************************************/ diff --git a/score/datarouter/include/dlt/dlt_protocol.h b/score/datarouter/include/dlt/dlt_protocol.h index 0c2e6b4..0d2409b 100644 --- a/score/datarouter/include/dlt/dlt_protocol.h +++ b/score/datarouter/include/dlt/dlt_protocol.h @@ -15,46 +15,6 @@ * @licence end@ */ -/*! - * \author Alexander Wenzel - * - * \copyright Copyright © 2011-2015 BMW AG. \n - * License MPL-2.0: Mozilla Public License version 2.0 http://mozilla.org/MPL/2.0/. - * - * \file dlt_protocol.h - */ - -/******************************************************************************* -** ** -** SRC-MODULE: dlt_protocol.h ** -** ** -** TARGET : linux ** -** ** -** PROJECT : DLT ** -** ** -** AUTHOR : Alexander Wenzel ** -** Markus Klein ** -** ** -** PURPOSE : ** -** ** -** REMARKS : ** -** ** -** PLATFORM DEPENDANT [yes/no]: yes ** -** ** -** TO BE CHANGED BY USER [yes/no]: no ** -** ** -*******************************************************************************/ - -/******************************************************************************* -** Author Identity ** -******************************************************************************** -** ** -** Initials Name Company ** -** -------- ------------------------- ---------------------------------- ** -** aw Alexander Wenzel BMW ** -** mk Markus Klein Fraunhofer ESK ** -*******************************************************************************/ - /******************************************************************************* ** Revision Control History ** *******************************************************************************/ diff --git a/score/datarouter/include/dlt/dlt_types.h b/score/datarouter/include/dlt/dlt_types.h index 1172c4f..7902579 100644 --- a/score/datarouter/include/dlt/dlt_types.h +++ b/score/datarouter/include/dlt/dlt_types.h @@ -15,46 +15,6 @@ * @licence end@ */ -/*! - * \author Alexander Wenzel - * - * \copyright Copyright © 2011-2015 BMW AG. \n - * License MPL-2.0: Mozilla Public License version 2.0 http://mozilla.org/MPL/2.0/. - * - * \file dlt_types.h - */ - -/******************************************************************************* -** ** -** SRC-MODULE: dlt_types.h ** -** ** -** TARGET : linux ** -** ** -** PROJECT : DLT ** -** ** -** AUTHOR : Alexander Wenzel ** -** Markus Klein ** -** ** -** PURPOSE : ** -** ** -** REMARKS : ** -** ** -** PLATFORM DEPENDANT [yes/no]: yes ** -** ** -** TO BE CHANGED BY USER [yes/no]: no ** -** ** -*******************************************************************************/ - -/******************************************************************************* -** Author Identity ** -******************************************************************************** -** ** -** Initials Name Company ** -** -------- ------------------------- ---------------------------------- ** -** aw Alexander Wenzel BMW ** -** mk Markus Klein Fraunhofer ESK ** -*******************************************************************************/ - #ifndef DLT_TYPES_H #define DLT_TYPES_H diff --git a/score/datarouter/include/logparser/i_logparser.h b/score/datarouter/include/logparser/i_logparser.h new file mode 100644 index 0000000..6adf300 --- /dev/null +++ b/score/datarouter/include/logparser/i_logparser.h @@ -0,0 +1,105 @@ +/******************************************************************************** + * 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 PAS_LOGGING_ILOGPARSER_H_ +#define PAS_LOGGING_ILOGPARSER_H_ + +#include "dlt/dltid.h" +#include "router/data_router_types.h" + +#include "score/mw/log/detail/data_router/shared_memory/shared_memory_reader.h" + +namespace score +{ +namespace mw +{ +namespace log +{ +namespace config +{ +class NvMsgDescriptor; +} +class INvConfig; +} // namespace log +} // namespace mw +namespace platform +{ + +using timestamp_t = score::os::HighResolutionSteadyClock::time_point; + +struct TypeInfo +{ + const score::mw::log::config::NvMsgDescriptor* nvMsgDesc; + bufsize_t id; + std::string params; + std::string typeName; + dltid_t ecuId; + dltid_t appId; +}; + +namespace internal +{ + +class ILogParser +{ + public: + class TypeHandler + { + public: + virtual void handle(timestamp_t timestamp, const char* data, bufsize_t size) = 0; + virtual ~TypeHandler() = default; + }; + + class AnyHandler + { + public: + virtual void handle(const TypeInfo& TypeInfo, timestamp_t timestamp, const char* data, bufsize_t size) = 0; + + virtual ~AnyHandler() = default; + }; + + virtual ~ILogParser() = default; + + // a function object to return whether the message parameter passes some encapsulted filter + // (in order to support content-based forwarding) + using FilterFunction = std::function; + + // a function to create such function objects based on the type of the message, + // the type of the filter object, and the serialized payload of the filter object + // (called on the request data provided by add_data_forwarder()) + using FilterFunctionFactory = std::function; + + virtual void set_filter_factory(FilterFunctionFactory factory) = 0; + + virtual void add_incoming_type(const bufsize_t map_index, const std::string& params) = 0; + virtual void AddIncomingType(const score::mw::log::detail::TypeRegistration&) = 0; + + virtual void add_type_handler(const std::string& typeName, TypeHandler& handler) = 0; + virtual void add_global_handler(AnyHandler& handler) = 0; + + virtual void remove_type_handler(const std::string& typeName, TypeHandler& handler) = 0; + virtual void remove_global_handler(AnyHandler& handler) = 0; + + virtual bool is_type_hndl_registered(const std::string& typeName, const TypeHandler& handler) = 0; + virtual bool is_glb_hndl_registered(const AnyHandler& handler) = 0; + + virtual void reset_internal_mapping() = 0; + virtual void parse(timestamp_t timestamp, const char* data, bufsize_t size) = 0; + virtual void Parse(const score::mw::log::detail::SharedMemoryRecord& record) = 0; +}; + +} // namespace internal +} // namespace platform +} // namespace score + +#endif // PAS_LOGGING_ILOGPARSER_H_ diff --git a/score/datarouter/include/logparser/logparser.h b/score/datarouter/include/logparser/logparser.h index af453ed..02eb7f5 100644 --- a/score/datarouter/include/logparser/logparser.h +++ b/score/datarouter/include/logparser/logparser.h @@ -14,10 +14,7 @@ #ifndef PAS_LOGGING_LOGPARSER_H_ #define PAS_LOGGING_LOGPARSER_H_ -#include "dlt/dltid.h" -#include "router/data_router_types.h" - -#include "score/mw/log/detail/data_router/shared_memory/shared_memory_reader.h" +#include "logparser/i_logparser.h" #include #include @@ -39,71 +36,36 @@ class INvConfig; namespace platform { -using timestamp_t = score::os::HighResolutionSteadyClock::time_point; - -struct TypeInfo -{ - const score::mw::log::config::NvMsgDescriptor* nvMsgDesc; - bufsize_t id; - std::string params; - std::string typeName; - dltid_t ecuId; - dltid_t appId; -}; - namespace internal { -class LogParser +class LogParser : public ILogParser { public: - class TypeHandler - { - public: - virtual void handle(timestamp_t timestamp, const char* data, bufsize_t size) = 0; - virtual ~TypeHandler() = default; - }; - - class AnyHandler - { - public: - virtual void handle(const TypeInfo& TypeInfo, timestamp_t timestamp, const char* data, bufsize_t size) = 0; - - virtual ~AnyHandler() = default; - }; - explicit LogParser(const score::mw::log::INvConfig& nv_config); + ~LogParser() = default; - // a function object to return whether the message parameter passes some encapsulted filter - // (in order to support content-based forwarding) - using FilterFunction = std::function; - - // a function to create such function objects based on the type of the message, - // the type of the filter object, and the serialized payload of the filter object - // (called on the request data provided by add_data_forwarder()) - using FilterFunctionFactory = std::function; - - void set_filter_factory(FilterFunctionFactory factory) + void set_filter_factory(FilterFunctionFactory factory) override { filter_factory = factory; } - void add_incoming_type(const bufsize_t map_index, const std::string& params); - void AddIncomingType(const score::mw::log::detail::TypeRegistration&); + void add_incoming_type(const bufsize_t map_index, const std::string& params) override; + void AddIncomingType(const score::mw::log::detail::TypeRegistration&) override; - void add_type_handler(const std::string& typeName, TypeHandler& handler); - void add_global_handler(AnyHandler& handler); + void add_type_handler(const std::string& typeName, TypeHandler& handler) override; + void add_global_handler(AnyHandler& handler) override; - void remove_type_handler(const std::string& typeName, TypeHandler& handler); - void remove_global_handler(AnyHandler& handler); + void remove_type_handler(const std::string& typeName, TypeHandler& handler) override; + void remove_global_handler(AnyHandler& handler) override; - bool is_type_hndl_registered(const std::string& typeName, const TypeHandler& handler); - bool is_glb_hndl_registered(const AnyHandler& handler); + bool is_type_hndl_registered(const std::string& typeName, const TypeHandler& handler) override; + bool is_glb_hndl_registered(const AnyHandler& handler) override; - void reset_internal_mapping(); + void reset_internal_mapping() override; - void parse(timestamp_t timestamp, const char* data, bufsize_t size); - void Parse(const score::mw::log::detail::SharedMemoryRecord& record); + void parse(timestamp_t timestamp, const char* data, bufsize_t size) override; + void Parse(const score::mw::log::detail::SharedMemoryRecord& record) override; private: struct HandleRequest diff --git a/score/datarouter/include/unix_domain/unix_domain_server.h b/score/datarouter/include/unix_domain/unix_domain_server.h index 75ed837..e0f4448 100644 --- a/score/datarouter/include/unix_domain/unix_domain_server.h +++ b/score/datarouter/include/unix_domain/unix_domain_server.h @@ -180,7 +180,7 @@ class UnixDomainServer static void process_idle_connections(ConnectionState& state); void cleanup_all_connections(ConnectionState& state); std::int32_t setup_server_socket(UnixDomainSockAddr& addr); - void process_server_iteration(ConnectionState& state, std::int32_t server_fd, std::int32_t timeout); + void process_server_iteration(ConnectionState& state, const std::int32_t server_fd, const std::int32_t timeout); void update_thread_name_server_routine() noexcept; std::atomic_bool server_exit_; diff --git a/score/datarouter/mocks/logparser_mock.h b/score/datarouter/mocks/logparser_mock.h new file mode 100644 index 0000000..f38c5f0 --- /dev/null +++ b/score/datarouter/mocks/logparser_mock.h @@ -0,0 +1,47 @@ +#ifndef PAS_LOGGING_LOGPARSERMOCK_H_ +#define PAS_LOGGING_LOGPARSERMOCK_H_ + +#include "logparser/i_logparser.h" + +#include +#include +#include + +#include + +namespace score +{ +namespace platform +{ +namespace internal +{ + +class LogParserMock : public ILogParser +{ + public: + ~LogParserMock() = default; + + MOCK_METHOD(void, set_filter_factory, (FilterFunctionFactory factory), (override)); + + MOCK_METHOD(void, add_incoming_type, (const bufsize_t map_index, const std::string& params), (override)); + MOCK_METHOD(void, AddIncomingType, (const score::mw::log::detail::TypeRegistration&), (override)); + + MOCK_METHOD(void, add_type_handler, (const std::string& typeName, TypeHandler& handler), (override)); + MOCK_METHOD(void, add_global_handler, (AnyHandler & handler), (override)); + + MOCK_METHOD(void, remove_type_handler, (const std::string& typeName, TypeHandler& handler), (override)); + MOCK_METHOD(void, remove_global_handler, (AnyHandler & handler), (override)); + + MOCK_METHOD(bool, is_type_hndl_registered, (const std::string& typeName, const TypeHandler& handler), (override)); + MOCK_METHOD(bool, is_glb_hndl_registered, (const AnyHandler& handler), (override)); + + MOCK_METHOD(void, reset_internal_mapping, (), (override)); + MOCK_METHOD(void, parse, (timestamp_t timestamp, const char* data, bufsize_t size), (override)); + MOCK_METHOD(void, Parse, (const score::mw::log::detail::SharedMemoryRecord& record), (override)); +}; + +} // namespace internal +} // namespace platform +} // namespace score + +#endif // PAS_LOGGING_LOGPARSERMOCK_H_ diff --git a/score/datarouter/nonverbose_dlt_stub/stub_nonverbose_dlt.h b/score/datarouter/nonverbose_dlt_stub/stub_nonverbose_dlt.h index 914a641..6bf0d2e 100644 --- a/score/datarouter/nonverbose_dlt_stub/stub_nonverbose_dlt.h +++ b/score/datarouter/nonverbose_dlt_stub/stub_nonverbose_dlt.h @@ -46,9 +46,7 @@ class StubDltNonverboseHandler : public LogParser::AnyHandler void handle(const TypeInfo&, timestamp_t, const char*, bufsize_t) override { - std::cerr - << "Warning: This is a stub implementation for the DltNonverboseHandler and the feature is not supported " - << std::endl; + // Stub implementation does nothing } }; diff --git a/score/datarouter/src/applications/main_nonadaptive.cpp b/score/datarouter/src/applications/main_nonadaptive.cpp index e3f2101..044e59e 100644 --- a/score/datarouter/src/applications/main_nonadaptive.cpp +++ b/score/datarouter/src/applications/main_nonadaptive.cpp @@ -39,7 +39,7 @@ int main(std::int32_t argc, const char* argv[]) if (!score::logging::options::Options::parse(argc, const_cast(argv))) { // Error messages have already been logged, just say goodbye. - score::mw::log::LogError() << args.front() << "Terminating because of errors in command line"; + score::mw::log::LogError() << std::string_view(args.front()) << "Terminating because of errors in command line"; return 1; } diff --git a/score/datarouter/src/daemon/message_passing_server.cpp b/score/datarouter/src/daemon/message_passing_server.cpp index d3e1556..ee89059 100644 --- a/score/datarouter/src/daemon/message_passing_server.cpp +++ b/score/datarouter/src/daemon/message_passing_server.cpp @@ -125,7 +125,11 @@ void MessagePassingServer::SessionWrapper::enqueue_tick_while_locked() // coverity[autosar_cpp14_a3_1_1_violation] MessagePassingServer::MessagePassingServer(MessagePassingServer::SessionFactory factory, ::score::concurrency::Executor& executor) - : factory_{std::move(factory)}, connection_timeout_{}, workers_exit_{false}, session_finishing_{false} + : IMessagePassingServerSessionWrapper(), + factory_{std::move(factory)}, + connection_timeout_{}, + workers_exit_{false}, + session_finishing_{false} { worker_thread_ = score::cpp::jthread([this]() { RunWorkerThread(); @@ -161,7 +165,20 @@ MessagePassingServer::MessagePassingServer(MessagePassingServer::SessionFactory } } -MessagePassingServer::~MessagePassingServer() +/* +Deviation from Rule A15-5-1: +- All user-provided class destructors, deallocation functions, move constructors, +- move assignment operators and swap functions shall not exit with an exception. +- A noexcept exception specification shall be added to these functions as appropriate. +Justification: +- Ensure that worker_thread_ is not running after destruction of MessagePassingServer +- checking worker_thread_.joinable() should be enough to avoid exception from join(). +- in this case join() could throw exception only if something goes wrong on OS level. +- this should be fine, moreover it could happen only on system shutdown stage +- and does not affect normal runtime +*/ +// coverity[autosar_cpp14_a15_5_1_violation] see above +MessagePassingServer::~MessagePassingServer() noexcept { // first, unblock the possible client connection requests { @@ -178,7 +195,10 @@ MessagePassingServer::~MessagePassingServer() workers_exit_ = true; } worker_cond_.notify_all(); - worker_thread_.join(); + if (worker_thread_.joinable()) + { + worker_thread_.join(); + } // finally, explicitly close all the remaining sessions pid_session_map_.clear(); diff --git a/score/datarouter/src/daemon/persistentlogging_config.cpp b/score/datarouter/src/daemon/persistentlogging_config.cpp index fefe54a..e659d05 100644 --- a/score/datarouter/src/daemon/persistentlogging_config.cpp +++ b/score/datarouter/src/daemon/persistentlogging_config.cpp @@ -57,11 +57,10 @@ PersistentLoggingConfig readPersistentLoggingConfig(const std::string& filePath) if (!ok) { score::mw::log::LogError() << "PersistentLoggingConfig:json parser error: " - << rapidjson::GetParseError_En(ok.Code()); + << std::string_view{rapidjson::GetParseError_En(ok.Code())}; config.readResult_ = ReadResult::ERROR_PARSE; return config; } - if (false == d.HasMember("verbose_filters") || false == d.HasMember("nonverbose_filters")) { score::mw::log::LogError() << "PersistentLoggingConfig: json filter members not found."; diff --git a/score/datarouter/src/daemon/socketserver.cpp b/score/datarouter/src/daemon/socketserver.cpp index 44f8b52..39671d5 100644 --- a/score/datarouter/src/daemon/socketserver.cpp +++ b/score/datarouter/src/daemon/socketserver.cpp @@ -34,6 +34,7 @@ #include "data_router_cfg.h" #include +#include #include namespace score @@ -93,26 +94,22 @@ std::string ResolveSharedMemoryFileName(const score::mw::log::detail::ConnectMes // LCOV_EXCL_STOP } // namespace -/* - - this is private functions in this file so it cannot be test. - - contains some concreate classes so we can not inject them. -*/ -// LCOV_EXCL_START -void SocketServer::doWork(const std::atomic_bool& exit_requested, const bool no_adaptive_runtime) +SocketServer::PersistentStorageHandlers SocketServer::InitializePersistentStorage( + std::unique_ptr& persistent_dictionary) { - SetThreadName(); - - score::mw::log::Logger& stats_logger = score::mw::log::CreateLogger("STAT", "statistics"); + PersistentStorageHandlers handlers; - std::unique_ptr pd = PersistentDictionaryFactoryType::Create(no_adaptive_runtime); - const auto loadDlt = [&pd]() { - return readDlt(*pd); + handlers.load_dlt = [&persistent_dictionary]() { + return readDlt(*persistent_dictionary); }; - const auto storeDlt = [&pd](const score::logging::dltserver::PersistentConfig& config) { - writeDlt(config, *pd); + + handlers.store_dlt = [&persistent_dictionary](const score::logging::dltserver::PersistentConfig& config) { + writeDlt(config, *persistent_dictionary); }; - bool isDltEnabled = readDltEnabled(*pd); + + handlers.is_dlt_enabled = readDltEnabled(*persistent_dictionary); + /* Deviation from Rule A16-0-1: - Rule A16-0-1 (required, implementation, automated) @@ -126,19 +123,41 @@ void SocketServer::doWork(const std::atomic_bool& exit_requested, const bool no_ // coverity[autosar_cpp14_a16_0_1_violation] #ifdef DLT_OUTPUT_ENABLED // TODO: will be reworked in Ticket-207823 - isDltEnabled = true; + handlers.is_dlt_enabled = true; // coverity[autosar_cpp14_a16_0_1_violation] see above #endif - score::mw::log::LogInfo() << "Loaded output enable = " << isDltEnabled; + score::mw::log::LogInfo() << "Loaded output enable = " << handlers.is_dlt_enabled; + + return handlers; +} + +std::unique_ptr SocketServer::CreateDltServer( + const PersistentStorageHandlers& storage_handlers) +{ const auto static_config = readStaticDlt(LOG_CHANNELS_PATH); if (!static_config.has_value()) { score::mw::log::LogError() << static_config.error(); - score::mw::log::LogError() << "Error during parsing file " << LOG_CHANNELS_PATH + score::mw::log::LogError() << "Error during parsing file " << std::string_view{LOG_CHANNELS_PATH} << ", static config is not available, interrupt work"; - return; + return nullptr; } + + /* + Deviation from Rule A5-1-4: + - A lambda expression object shall not outlive any of its reference captured objects. + Justification: + - dltServer and lambda are in the same scope. + */ + // coverity[autosar_cpp14_a5_1_4_violation] + return std::make_unique( + static_config.value(), storage_handlers.load_dlt, storage_handlers.store_dlt, storage_handlers.is_dlt_enabled); +} + +DataRouter::SourceSetupCallback SocketServer::CreateSourceSetupHandler( + score::logging::dltserver::DltLogServer& dlt_server) +{ /* Deviation from Rule A5-1-4: - A lambda expression object shall not outlive any of its reference captured objects. @@ -146,11 +165,38 @@ void SocketServer::doWork(const std::atomic_bool& exit_requested, const bool no_ - dltServer and lambda are in the same scope. */ // coverity[autosar_cpp14_a5_1_4_violation] - score::logging::dltserver::DltLogServer dltServer(static_config.value(), loadDlt, storeDlt, isDltEnabled); - const auto sourceSetup = [&dltServer](LogParser&& parser) { + return [&dlt_server](score::platform::internal::ILogParser&& parser) { parser.set_filter_factory(getFilterFactory()); - dltServer.add_handlers(parser); + dlt_server.add_handlers(parser); }; +} + +// Static helper: Update handlers for each parser +void SocketServer::UpdateParserHandlers(score::logging::dltserver::DltLogServer& dlt_server, + score::platform::internal::ILogParser& parser, + bool enable) +{ + dlt_server.update_handlers(parser, enable); +} + +// Static helper: Final update after all parsers processed +void SocketServer::UpdateHandlersFinal(score::logging::dltserver::DltLogServer& dlt_server, bool enable) +{ + dlt_server.update_handlers_final(enable); +} + +// Static helper: Create a new config session from Unix domain handle +std::unique_ptr SocketServer::CreateConfigSession( + score::logging::dltserver::DltLogServer& dlt_server, + UnixDomainServer::SessionHandle handle) +{ + return dlt_server.new_config_session(score::platform::datarouter::ConfigSessionHandleType{std::move(handle)}); +} + +std::function SocketServer::CreateEnableHandler(DataRouter& router, + IPersistentDictionary& persistent_dictionary, + score::logging::dltserver::DltLogServer& dlt_server) +{ /* Deviation from Rule A5-1-4: - A lambda expression object shall not outlive any of its reference captured objects. @@ -158,8 +204,7 @@ void SocketServer::doWork(const std::atomic_bool& exit_requested, const bool no_ - router and lambda are in the same scope. */ // coverity[autosar_cpp14_a5_1_4_violation] - DataRouter router(stats_logger, sourceSetup); - const auto enableHandler = [&router, &pd, &dltServer](bool enable) { + return [&router, &persistent_dictionary, &dlt_server](bool enable) { /* Deviation from Rule A16-0-1: - Rule A16-0-1 (required, implementation, automated) @@ -178,21 +223,19 @@ void SocketServer::doWork(const std::atomic_bool& exit_requested, const bool no_ #endif std::cerr << "DRCMD enable callback called with " << enable << std::endl; score::mw::log::LogWarn() << "Changing output enable to " << enable; - writeDltEnabled(enable, *pd); + writeDltEnabled(enable, persistent_dictionary); router.for_each_source_parser( - [&dltServer, enable](LogParser& parser) { - dltServer.update_handlers(parser, enable); - }, - [&dltServer, enable] { - dltServer.update_handlers_final(enable); - }, + std::bind(&SocketServer::UpdateParserHandlers, std::ref(dlt_server), std::placeholders::_1, enable), + std::bind(&SocketServer::UpdateHandlersFinal, std::ref(dlt_server), enable), enable); }; - dltServer.set_enabled_callback(enableHandler); +} + +std::unique_ptr SocketServer::CreateUnixDomainServer( + score::logging::dltserver::DltLogServer& dlt_server) +{ + const auto factory = std::bind(&SocketServer::CreateConfigSession, std::ref(dlt_server), std::placeholders::_2); - const auto factory = [&dltServer](const std::string& /*name*/, UnixDomainServer::SessionHandle handle) { - return dltServer.new_config_session(score::platform::datarouter::ConfigSessionHandleType{std::move(handle)}); - }; const UnixDomainSockAddr addr(score::logging::config::socket_address, true); /* Deviation from Rule A5-1-4: @@ -201,11 +244,14 @@ void SocketServer::doWork(const std::atomic_bool& exit_requested, const bool no_ - server does not exist inside any lambda. */ // coverity[autosar_cpp14_a5_1_4_violation: FALSE] - UnixDomainServer server(addr, factory); + return std::make_unique(addr, factory); +} - // Try to create NvConfig from file using factory, fallback to empty config if it fails - const auto nvConfig = [&stats_logger]() { - auto nvConfigResult = score::mw::log::NvConfigFactory::CreateAndInit(); +score::mw::log::NvConfig SocketServer::LoadNvConfig(score::mw::log::Logger& stats_logger, const std::string& config_path) +{ + if constexpr (score::platform::datarouter::kNonVerboseDltEnabled) + { + auto nvConfigResult = score::mw::log::NvConfigFactory::CreateAndInit(config_path); if (nvConfigResult.has_value()) { stats_logger.LogInfo() << "NvConfig loaded successfully"; @@ -214,65 +260,67 @@ void SocketServer::doWork(const std::atomic_bool& exit_requested, const bool no_ else { stats_logger.LogWarn() << "Failed to load NvConfig: " << nvConfigResult.error().Message(); - return score::mw::log::NvConfigFactory::CreateEmpty(); - } - }(); // ← immediately invoked - - const auto mp_factory = [&router, &dltServer, &nvConfig]( - const pid_t client_pid, - const score::mw::log::detail::ConnectMessageFromClient& conn, - score::cpp::pmr::unique_ptr handle) - -> std::unique_ptr { - const auto appid_sv = conn.GetAppId().GetStringView(); - const std::string appid{appid_sv.data(), appid_sv.size()}; - const std::string shared_memory_file_name = ResolveSharedMemoryFileName(conn, appid); - // The reason for banning is, because it's error-prone to use. One should use abstractions e.g. provided by - // the C++ standard library. But these abstraction do not support exclusive access, which is why we created - // this abstraction library. - // NOLINTBEGIN(score-banned-function): See above. - auto maybe_fd = - score::os::Fcntl::instance().open(shared_memory_file_name.c_str(), score::os::Fcntl::Open::kReadOnly); - // NOLINTEND(score-banned-function) it is among safety headers. - if (!maybe_fd.has_value()) - { - std::cerr << "message_session_factory: open(O_RDONLY) " << shared_memory_file_name << maybe_fd.error() - << std::endl; - return std::unique_ptr(); } + } + return score::mw::log::NvConfigFactory::CreateEmpty(); +} - const auto fd = maybe_fd.value(); - const auto quota = dltServer.get_quota(appid); - const auto quotaEnforcementEnabled = dltServer.getQuotaEnforcementEnabled(); - const bool is_dlt_enabled = dltServer.GetDltEnabled(); - auto source_session = router.new_source_session( - fd, appid, is_dlt_enabled, std::move(handle), quota, quotaEnforcementEnabled, client_pid, nvConfig); - // The reason for banning is, because it's error-prone to use. One should use abstractions e.g. provided by - // the C++ standard library. But these abstraction do not support exclusive access, which is why we created - // this abstraction library. - // NOLINTNEXTLINE(score-banned-function): See above. - const auto close_result = score::os::Unistd::instance().close(fd); - if (close_result.has_value() == false) - { - std::cerr << "message_session_factory: close(" << shared_memory_file_name - << ") failed: " << close_result.error() << std::endl; - } - return source_session; - }; +// Static helper: Create a message passing session from connection info +std::unique_ptr SocketServer::CreateMessagePassingSession( + DataRouter& router, + score::logging::dltserver::DltLogServer& dlt_server, + const score::mw::log::NvConfig& nv_config, + const pid_t client_pid, + const score::mw::log::detail::ConnectMessageFromClient& conn, + score::cpp::pmr::unique_ptr handle) +{ + const auto appid_sv = conn.GetAppId().GetStringView(); + const std::string appid{appid_sv.data(), appid_sv.size()}; + const std::string shared_memory_file_name = ResolveSharedMemoryFileName(conn, appid); + // The reason for banning is, because it's error-prone to use. One should use abstractions e.g. provided by + // the C++ standard library. But these abstraction do not support exclusive access, which is why we created + // this abstraction library. + // NOLINTBEGIN(score-banned-function): See above. + auto maybe_fd = score::os::Fcntl::instance().open(shared_memory_file_name.c_str(), score::os::Fcntl::Open::kReadOnly); + // NOLINTEND(score-banned-function) it is among safety headers. + if (!maybe_fd.has_value()) + { + std::cerr << "message_session_factory: open(O_RDONLY) " << shared_memory_file_name << maybe_fd.error() + << std::endl; + return std::unique_ptr(); + } - // 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}; - /* - Deviation from Rule A5-1-4: - - A lambda expression object shall not outlive any of its reference captured objects. - Justification: - - mp_server does not exist inside any lambda. - */ - // coverity[autosar_cpp14_a5_1_4_violation: FALSE] - MessagePassingServer mp_server(mp_factory, executor); + const auto fd = maybe_fd.value(); + const auto quota = dlt_server.get_quota(appid); + const auto quotaEnforcementEnabled = dlt_server.getQuotaEnforcementEnabled(); + const bool is_dlt_enabled = dlt_server.GetDltEnabled(); + auto source_session = router.new_source_session( + fd, appid, is_dlt_enabled, std::move(handle), quota, quotaEnforcementEnabled, client_pid, nv_config); + // The reason for banning is, because it's error-prone to use. One should use abstractions e.g. provided by + // the C++ standard library. But these abstraction do not support exclusive access, which is why we created + // this abstraction library. + // NOLINTNEXTLINE(score-banned-function): See above. + const auto close_result = score::os::Unistd::instance().close(fd); + if (close_result.has_value() == false) + { + std::cerr << "message_session_factory: close(" << shared_memory_file_name + << ") failed: " << close_result.error() << std::endl; + } + return source_session; +} +/* + RunEventLoop and doWork are integration-level orchestration functions. + They are tested through integration tests. All individual functions they call + (InitializePersistentStorage, CreateDltServer, CreateEnableHandler, etc.) + are already tested at 100% coverage in unit tests. +*/ +// LCOV_EXCL_START +void SocketServer::RunEventLoop(const std::atomic_bool& exit_requested, + DataRouter& router, + score::logging::dltserver::DltLogServer& dlt_server, + score::mw::log::Logger& stats_logger) +{ uint16_t count = 0U; constexpr std::uint32_t statistics_freq_divider = statistics_log_period_us / throttle_time_us; constexpr std::uint32_t dlt_freq_divider = dlt_flush_period_us / throttle_time_us; @@ -284,15 +332,81 @@ void SocketServer::doWork(const std::atomic_bool& exit_requested, const bool no_ if ((count % statistics_freq_divider) == 0U) { router.show_source_statistics(static_cast(count / statistics_freq_divider)); - dltServer.show_channel_statistics(static_cast(count / statistics_freq_divider), stats_logger); + dlt_server.show_channel_statistics(static_cast(count / statistics_freq_divider), stats_logger); } if ((count % dlt_freq_divider) == 0U) { - dltServer.flush(); + dlt_server.flush(); } ++count; } } + +void SocketServer::doWork(const std::atomic_bool& exit_requested, const bool no_adaptive_runtime) +{ + SetThreadName(); + + score::mw::log::Logger& stats_logger = score::mw::log::CreateLogger("STAT", "statistics"); + + // Initialize persistent storage + std::unique_ptr pd = PersistentDictionaryFactoryType::Create(no_adaptive_runtime); + const PersistentStorageHandlers storage_handlers = InitializePersistentStorage(pd); + + // Create DLT server + auto dlt_server = CreateDltServer(storage_handlers); + if (!dlt_server) + { + return; + } + + // Create data router with source setup handler + const auto source_setup = CreateSourceSetupHandler(*dlt_server); + /* + Deviation from Rule A5-1-4: + - A lambda expression object shall not outlive any of its reference captured objects. + Justification: + - router and lambda are in the same scope. + */ + // coverity[autosar_cpp14_a5_1_4_violation] + DataRouter router(stats_logger, source_setup); + + // Create and set enable handler + const auto enable_handler = CreateEnableHandler(router, *pd, *dlt_server); + dlt_server->set_enabled_callback(enable_handler); + + // Create Unix domain server for config sessions + auto unix_domain_server = CreateUnixDomainServer(*dlt_server); + + // Load NvConfig + const score::mw::log::NvConfig nv_config = LoadNvConfig(stats_logger); + + // Create message passing factory using std::bind directly + const auto mp_factory = std::bind(&SocketServer::CreateMessagePassingSession, + std::ref(router), + std::ref(*dlt_server), + std::ref(nv_config), + std::placeholders::_1, // client_pid + 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}; + /* + Deviation from Rule A5-1-4: + - A lambda expression object shall not outlive any of its reference captured objects. + Justification: + - mp_server does not exist inside any lambda. + */ + // coverity[autosar_cpp14_a5_1_4_violation: FALSE] + MessagePassingServer mp_server(mp_factory, executor); + + // Run main event loop + RunEventLoop(exit_requested, router, *dlt_server, stats_logger); +} // LCOV_EXCL_STOP } // namespace datarouter diff --git a/score/datarouter/src/daemon/socketserver_config.cpp b/score/datarouter/src/daemon/socketserver_config.cpp index da58c9c..1ce6e50 100644 --- a/score/datarouter/src/daemon/socketserver_config.cpp +++ b/score/datarouter/src/daemon/socketserver_config.cpp @@ -15,6 +15,7 @@ #include "daemon/socketserver_json_helpers.h" #include "score/datarouter/error/error.h" +#include "score/datarouter/include/applications/datarouter_feature_config.h" #include "score/datarouter/include/daemon/utility.h" #include @@ -337,7 +338,10 @@ void writeDlt(const score::logging::dltserver::PersistentConfig& config, IPersis bool readDltEnabled(IPersistentDictionary& pd) { const bool enabled = pd.getBool(CONFIG_OUTPUT_ENABLED_KEY, true); - std::cout << "Loaded output enable = " << enabled << " from KVS" << std::endl; + if constexpr (kPersistentConfigFeatureEnabled) + { + std::cout << "Loaded output enable = " << enabled << " from KVS" << std::endl; + } return enabled; } diff --git a/score/datarouter/src/daemon/udp_stream_output.cpp b/score/datarouter/src/daemon/udp_stream_output.cpp index ce68b4f..9a6bb58 100644 --- a/score/datarouter/src/daemon/udp_stream_output.cpp +++ b/score/datarouter/src/daemon/udp_stream_output.cpp @@ -89,33 +89,53 @@ score::logging::dltserver::UdpStreamOutput::UdpStreamOutput(const char* dstAddr, } } - if (multicastInterface != nullptr && std::strlen(multicastInterface) != 0) + if (multicastInterface != nullptr) { - struct in_addr addr{}; - if (inet_aton(multicastInterface, &addr) != 0) + const std::string_view strViewMulticastInterface{multicastInterface}; + if (strViewMulticastInterface.length() != 0) { - if (setsockopt(socket_, IPPROTO_IP, IP_MULTICAST_IF, &addr, sizeof(addr)) == -1) + struct in_addr addr{}; + if (inet_aton(multicastInterface, &addr) != 0) { - std::cerr << "ERROR: (UDP) socket cannot use multicast interface: " - /* - Deviation from Rule M19-3-1: - - The error indicator errno shall not be used. - Justification: - - Using library-defined macro to ensure correct operation. - */ + if (setsockopt(socket_, IPPROTO_IP, IP_MULTICAST_IF, &addr, sizeof(addr)) == -1) + { + std::cerr << "ERROR: (UDP) socket cannot use multicast interface: " + /* + Deviation from Rule M19-3-1: + - The error indicator errno shall not be used. + Justification: + - Using library-defined macro to ensure correct operation. + */ + // coverity[autosar_cpp14_m19_3_1_violation] + << std::system_category().message(errno) << std::endl; + } + } + else + { + std::cerr << "ERROR: Invalid multicast interface address: " << multicastInterface + << " " // coverity[autosar_cpp14_m19_3_1_violation] << std::system_category().message(errno) << std::endl; } } - else - { - std::cerr << "ERROR: Invalid multicast interface address: " << multicastInterface - << " " - // coverity[autosar_cpp14_m19_3_1_violation] - << std::system_category().message(errno) << std::endl; - } } - +/* +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: +- Implementation selection for different OS and respective versions. +*/ +// coverity[autosar_cpp14_a16_0_1_violation] see above +#if defined(__QNX__) && __QNX__ >= 800 + // In QNX 8.0, the vlan priority cannot be set per socket, but by interface. Hence SetVlanPriorityOfSocket is + // skipped. + static_cast(vlan); +// coverity[autosar_cpp14_a16_0_1_violation] see above +#else constexpr std::uint8_t kDltPcpPriority = 1u; const auto pcp_result = vlan.SetVlanPriorityOfSocket(kDltPcpPriority, socket_); if (!pcp_result.has_value()) @@ -123,6 +143,8 @@ score::logging::dltserver::UdpStreamOutput::UdpStreamOutput(const char* dstAddr, const auto error = pcp_result.error().ToString(); std::cerr << "ERROR: Setting PCP Priority: " << error << std::endl; } +// coverity[autosar_cpp14_a16_0_1_violation] see above +#endif } score::logging::dltserver::UdpStreamOutput::~UdpStreamOutput() diff --git a/score/datarouter/src/logparser/logparser.cpp b/score/datarouter/src/logparser/logparser.cpp index 59b2b39..f18542a 100644 --- a/score/datarouter/src/logparser/logparser.cpp +++ b/score/datarouter/src/logparser/logparser.cpp @@ -207,6 +207,14 @@ void LogParser::parse(timestamp_t timestamp, const char* data, bufsize_t size) return; } bufsize_t index = 0U; + /* + Deviation from Rule M5-0-16: + - A pointer operand and any pointer resulting from pointer arithmetic using that operand shall both address elements + of the same array. Justification: + - type case is necessary to extract index of parser from raw memory block (input data). + - std::copy_n() uses same array for output, different array warning comes from score::cpp::bit_cast usage. + */ + // coverity[autosar_cpp14_m5_0_16_violation] see above std::copy_n(data, sizeof(index), score::cpp::bit_cast(&index)); std::advance(data, sizeof(index)); diff --git a/score/datarouter/src/unix_domain/unix_domain_server.cpp b/score/datarouter/src/unix_domain/unix_domain_server.cpp index bb547fc..49bf6cc 100644 --- a/score/datarouter/src/unix_domain/unix_domain_server.cpp +++ b/score/datarouter/src/unix_domain/unix_domain_server.cpp @@ -188,9 +188,20 @@ std::int32_t UnixDomainServer::setup_server_socket(UnixDomainSockAddr& addr) // NOLINTNEXTLINE(score-banned-function): Suppressed here because of error handling std::exit(EXIT_FAILURE); } + const std::int32_t server_fd = socket_ret.value(); const auto bind_ret = score::os::Socket::instance().bind( - server_fd, static_cast(static_cast(&addr)), sizeof(sockaddr_un)); + server_fd, + /* + Deviation from Rule A5-2-8: + - An object with integer type or pointer to void type shall not be converted to an object with pointer type. + Justification: + - type case is necessary to convert pointer to internal type (UnixDomainSockAddr) to pointer to sockaddr type + - which is required for score::os::Socket::instance()::bind() + */ + // coverity[autosar_cpp14_m5_2_8_violation] see above + static_cast(static_cast(&addr)), + sizeof(sockaddr_un)); if (!bind_ret.has_value()) { std::perror("bind"); diff --git a/score/datarouter/test/ut/ut_logging/BUILD b/score/datarouter/test/ut/ut_logging/BUILD index 681ab61..31d02bb 100644 --- a/score/datarouter/test/ut/ut_logging/BUILD +++ b/score/datarouter/test/ut/ut_logging/BUILD @@ -38,6 +38,7 @@ test_suite( ":messagePassingServerUT", ":persistentLogConfigUT", ":socketserverConfigUT", + ":socketserverUT", ":udp_stream_output_test", ":unix_domain_server_test", ":utility_test", @@ -296,6 +297,25 @@ cc_test( ], ) +cc_test( + name = "socketserverUT", + srcs = [ + "test_socketserver.cpp", + ], + data = [ + "//score/datarouter/test/ut/etc:fg_socket_server_log_channels_test_json", + ], + features = FEAT_COMPILER_WARNINGS_AS_ERRORS, + tags = ["unit"], + deps = [ + "//score/datarouter:datarouter_socketserver_testing", + "//score/datarouter/src/persistency:mock", + "@googletest//:gtest_main", + "@score_baselibs//score/mw/log/configuration:nvconfig_mock", + "@score_baselibs//score/os/mocklib:unistd_mock", + ], +) + cc_test( name = "messagePassingServerUT", srcs = [ @@ -410,6 +430,7 @@ py_unittest_qnx_test( ":unix_domain_server_test", ":messagePassingServerUT", ":socketserverConfigUT", + ":socketserverUT", ":log_entry_deserialize_test", ":utility_test", ":FileTransferHandlerFactoryUT", diff --git a/score/datarouter/test/ut/ut_logging/test_socketserver.cpp b/score/datarouter/test/ut/ut_logging/test_socketserver.cpp new file mode 100644 index 0000000..9048f74 --- /dev/null +++ b/score/datarouter/test/ut/ut_logging/test_socketserver.cpp @@ -0,0 +1,568 @@ +#include "daemon/socketserver.h" +#include "logparser/logparser.h" +#include "score/os/mocklib/unistdmock.h" +#include "score/mw/log/configuration/invconfig_mock.h" +#include "score/datarouter/datarouter/data_router.h" +#include "score/datarouter/src/persistency/mock_persistent_dictionary.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include +#include + +using namespace testing; + +namespace score +{ +namespace platform +{ +namespace datarouter +{ +namespace +{ + +const std::string CONFIG_DATABASE_KEY = "dltConfig"; +const std::string CONFIG_OUTPUT_ENABLED_KEY = "dltOutputEnabled"; + +class SocketServerInitializePersistentStorageTest : public ::testing::Test +{ + protected: + void SetUp() override + { + mock_pd_ = std::make_unique>(); + } + + std::unique_ptr mock_pd_; +}; + +TEST_F(SocketServerInitializePersistentStorageTest, InitializeWithDltEnabled) +{ + RecordProperty("Description", "Verify InitializePersistentStorage creates handlers with DLT enabled"); + RecordProperty("TestType", "Interface test"); + RecordProperty("Verifies", "::score::platform::datarouter::SocketServer::InitializePersistentStorage()"); + 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)) + .WillOnce(Return(true)); + + auto handlers = SocketServer::InitializePersistentStorage(mock_pd_); + + // Verify is_dlt_enabled is set correctly +#ifdef DLT_OUTPUT_ENABLED + // When DLT_OUTPUT_ENABLED is defined, it should always be true + EXPECT_TRUE(handlers.is_dlt_enabled); +#else + EXPECT_TRUE(handlers.is_dlt_enabled); +#endif + + // Verify load_dlt lambda is callable + EXPECT_TRUE(handlers.load_dlt); + + // Verify store_dlt lambda is callable + EXPECT_TRUE(handlers.store_dlt); +} + +TEST_F(SocketServerInitializePersistentStorageTest, InitializeWithDltDisabled) +{ + RecordProperty("Description", "Verify InitializePersistentStorage creates handlers with DLT disabled"); + RecordProperty("TestType", "Interface test"); + RecordProperty("Verifies", "::score::platform::datarouter::SocketServer::InitializePersistentStorage()"); + 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)) + .WillOnce(Return(false)); + + auto handlers = SocketServer::InitializePersistentStorage(mock_pd_); + + // Verify is_dlt_enabled is set correctly +#ifdef DLT_OUTPUT_ENABLED + // When DLT_OUTPUT_ENABLED is defined, it should always be true regardless of persistent storage + EXPECT_TRUE(handlers.is_dlt_enabled); +#else + EXPECT_FALSE(handlers.is_dlt_enabled); +#endif + + // Verify load_dlt lambda is callable + EXPECT_TRUE(handlers.load_dlt); + + // Verify store_dlt lambda is callable + EXPECT_TRUE(handlers.store_dlt); +} + +TEST_F(SocketServerInitializePersistentStorageTest, LoadDltLambdaCallsReadDlt) +{ + RecordProperty("Description", "Verify load_dlt lambda calls readDlt correctly"); + RecordProperty("TestType", "Interface test"); + RecordProperty("Verifies", "::score::platform::datarouter::SocketServer::InitializePersistentStorage()"); + 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)) + .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, _)) + .WillOnce(Return("{}")); + + // Call the load_dlt lambda - it should successfully return a PersistentConfig + auto config = handlers.load_dlt(); + + // Verify the lambda executed and returned a config (structure is opaque, just verify it returned) + SUCCEED(); +} + +TEST_F(SocketServerInitializePersistentStorageTest, StoreDltLambdaCallsWriteDlt) +{ + RecordProperty("Description", "Verify store_dlt lambda calls writeDlt correctly"); + RecordProperty("TestType", "Interface test"); + RecordProperty("Verifies", "::score::platform::datarouter::SocketServer::InitializePersistentStorage()"); + 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)) + .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); + + // Create a test config + score::logging::dltserver::PersistentConfig test_config; + + // Call the store_dlt lambda + handlers.store_dlt(test_config); + + // Verify the lambda executed successfully (mock expectation verified in TearDown) +} + +// Test fixture for CreateDltServer tests +class SocketServerCreateDltServerTest : public ::testing::Test +{ + protected: + void SetUp() override + { + // Copy the real test config file to ./etc/log-channels.json + ::mkdir("./etc", 0755); // Ignore error if exists + + // Use the real config file from test data + std::ifstream src("score/datarouter/test/ut/etc/log-channels.json", std::ios::binary); + std::ofstream dst("./etc/log-channels.json", std::ios::binary); + dst << src.rdbuf(); + src.close(); + dst.close(); + } + + void TearDown() override + { + // Clean up + ::remove("./etc/log-channels.json"); + ::rmdir("./etc"); + } + + SocketServer::PersistentStorageHandlers CreateTestHandlers() + { + // Create minimal handlers for testing + SocketServer::PersistentStorageHandlers handlers; + handlers.load_dlt = []() { + return score::logging::dltserver::PersistentConfig{}; + }; + handlers.store_dlt = [](const score::logging::dltserver::PersistentConfig&) {}; + handlers.is_dlt_enabled = true; + return handlers; + } +}; + +TEST_F(SocketServerCreateDltServerTest, CreateDltServerExecutesSuccessfully) +{ + RecordProperty( + "Description", + "Verify CreateDltServer returns correct type and CreateSourceSetupHandler works when DltServer exists"); + RecordProperty("TestType", "Interface test"); + RecordProperty("Verifies", "::score::platform::datarouter::SocketServer::CreateDltServer()"); + RecordProperty("DerivationTechnique", "Analysis of boundary values"); + + auto handlers = CreateTestHandlers(); + + // Call CreateDltServer - it will attempt to read from ./etc/log-channels.json + auto dlt_server = SocketServer::CreateDltServer(handlers); + + // Verify correct return type: std::unique_ptr + EXPECT_TRUE((std::is_same>::value)); + + // If DltServer was created successfully, test CreateSourceSetupHandler + ASSERT_TRUE(dlt_server != nullptr); + + // Call CreateSourceSetupHandler with the created DltServer (lines 144-154) + auto source_setup_handler = SocketServer::CreateSourceSetupHandler(*dlt_server); + + // Verify correct return type + EXPECT_TRUE((std::is_same::value)); + + // Verify the lambda was created (not null) + EXPECT_TRUE(static_cast(source_setup_handler)); + + // Execute the lambda to cover lines 152-153 + score::mw::log::INvConfigMock nvconfig_mock; + score::platform::internal::LogParser parser(nvconfig_mock); + + // Call the lambda + source_setup_handler(std::move(parser)); +} + +TEST_F(SocketServerCreateDltServerTest, CreateDltServerReturnsNullOnConfigError) +{ + RecordProperty("Description", "Verify CreateDltServer returns nullptr when config file is invalid"); + RecordProperty("TestType", "Fault injection test"); + RecordProperty("Verifies", "::score::platform::datarouter::SocketServer::CreateDltServer()"); + RecordProperty("DerivationTechnique", "Error guessing based on knowledge or experience"); + RecordProperty("InjectionPoints", "Missing config file"); + RecordProperty("MeasurementPoints", "Function returns nullptr"); + + // Remove the config file to force readStaticDlt to fail + ::remove("./etc/log-channels.json"); + + auto handlers = CreateTestHandlers(); + + // Call CreateDltServer - should fail due to missing config + auto dlt_server = SocketServer::CreateDltServer(handlers); + + // Verify it returns nullptr on error + EXPECT_EQ(dlt_server, nullptr); +} + +// Test fixture for remaining functions +class SocketServerRemainingFunctionsTest : public SocketServerCreateDltServerTest +{ + protected: + void SetUp() override + { + SocketServerCreateDltServerTest::SetUp(); + + // Create a simple test NvConfig file in current directory + test_config_path_ = "./test-class-id.json"; + std::ofstream config_file(test_config_path_); + config_file << R"({ + "score::logging::PersistentLogFileEvent": { + "id": 301, + "ctxid": "PERL", + "appid": "DRC", + "loglevel": 1 + } +})"; + config_file.close(); + + // Create test handlers for use in child tests + storage_handlers_ = CreateTestHandlers(); + + // Create mock persistent dictionary for CreateEnableHandler test + mock_pd_ = std::make_unique>(); + } + + void TearDown() override + { + // Clean up test config file + ::remove(test_config_path_.c_str()); + + SocketServerCreateDltServerTest::TearDown(); + } + + std::string test_config_path_; + SocketServer::PersistentStorageHandlers storage_handlers_; + std::unique_ptr mock_pd_; +}; + +TEST_F(SocketServerRemainingFunctionsTest, LoadNvConfigSuccessPath) +{ + RecordProperty("Description", "Verify LoadNvConfig success path with valid config file"); + RecordProperty("TestType", "Interface test"); + RecordProperty("Verifies", "::score::platform::datarouter::SocketServer::LoadNvConfig()"); + RecordProperty("DerivationTechnique", "Error guessing based on knowledge or experience"); + + score::mw::log::Logger& logger = score::mw::log::CreateLogger("TEST", "test"); + + // Call LoadNvConfig with valid test data - should succeed (lines 225-226) + auto nv_config = SocketServer::LoadNvConfig(logger, test_config_path_); + + // Verify that we got a valid config by checking for a known type from test-class-id.json + // The test data contains "score::logging::PersistentLogFileEvent" + const auto* descriptor = nv_config.getDltMsgDesc("score::logging::PersistentLogFileEvent"); + EXPECT_NE(nullptr, descriptor); // Should find the entry +} + +TEST_F(SocketServerRemainingFunctionsTest, LoadNvConfigErrorPath) +{ + RecordProperty("Description", "Verify LoadNvConfig error path with invalid config file"); + RecordProperty("TestType", "Fault injection test"); + RecordProperty("Verifies", "::score::platform::datarouter::SocketServer::LoadNvConfig()"); + RecordProperty("DerivationTechnique", "Error guessing based on knowledge or experience"); + RecordProperty("InjectionPoints", "Invalid config file path"); + RecordProperty("MeasurementPoints", "Function handles error gracefully"); + + score::mw::log::Logger& logger = score::mw::log::CreateLogger("TEST", "test"); + + // Call LoadNvConfig with invalid path - should fail (lines 230-231) + auto nv_config = SocketServer::LoadNvConfig(logger, "/nonexistent/path/class-id.json"); + + // Verify that we got an empty config by checking for any type + const auto* descriptor = nv_config.getDltMsgDesc("score::logging::PersistentLogFileEvent"); + EXPECT_EQ(nullptr, descriptor); // Empty config returns nullptr for all queries +} + +TEST_F(SocketServerRemainingFunctionsTest, CreateUnixDomainServerExecutesSuccessfully) +{ + RecordProperty("Description", "Verify CreateUnixDomainServer creates UnixDomainServer instance"); + RecordProperty("TestType", "Interface test"); + RecordProperty("Verifies", "::score::platform::datarouter::SocketServer::CreateUnixDomainServer()"); + RecordProperty("DerivationTechnique", "Error guessing based on knowledge or experience"); + + // CreateUnixDomainServer needs a DltLogServer + auto dlt_server = SocketServer::CreateDltServer(storage_handlers_); + ASSERT_NE(nullptr, dlt_server); + + // Call CreateUnixDomainServer - this covers lines 202-217 + // The function creates a UnixDomainServer with a lambda factory + auto unix_domain_server = SocketServer::CreateUnixDomainServer(*dlt_server); + + // Verify that the server was created (covers all lines in the function) + EXPECT_NE(nullptr, unix_domain_server); +} + +TEST_F(SocketServerRemainingFunctionsTest, CreateEnableHandlerCreatesCallbackSuccessfully) +{ + RecordProperty("Description", "Verify CreateEnableHandler creates and executes callback function"); + RecordProperty("TestType", "Interface test"); + RecordProperty("Verifies", "::score::platform::datarouter::SocketServer::CreateEnableHandler()"); + RecordProperty("DerivationTechnique", "Error guessing based on knowledge or experience"); + + // Create DltLogServer for the handler + auto dlt_server = SocketServer::CreateDltServer(storage_handlers_); + ASSERT_NE(nullptr, dlt_server); + + // Create a minimal DataRouter + score::mw::log::Logger& logger = score::mw::log::CreateLogger("TEST", "test"); + const auto source_setup = SocketServer::CreateSourceSetupHandler(*dlt_server); + 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, _)) + .Times(1); + + // Create the enable handler - this covers lines 160-171 (function body and lambda creation) + auto enable_handler = SocketServer::CreateEnableHandler(router, *mock_pd_, *dlt_server); + + // Verify the lambda was created (callable) + EXPECT_TRUE(static_cast(enable_handler)); + + // Invoke the lambda to cover lines 171-199 (lambda body execution) + // This will call writeDltEnabled and router.for_each_source_parser + enable_handler(true); +} + +TEST_F(SocketServerRemainingFunctionsTest, UpdateParserHandlersExecutesSuccessfully) +{ + RecordProperty("Description", "Verify UpdateParserHandlers static function works correctly"); + RecordProperty("TestType", "Interface test"); + RecordProperty("Verifies", "::score::platform::datarouter::SocketServer::UpdateParserHandlers()"); + RecordProperty("DerivationTechnique", "Error guessing based on knowledge or experience"); + + // Create DltLogServer + auto dlt_server = SocketServer::CreateDltServer(storage_handlers_); + ASSERT_NE(nullptr, dlt_server); + + // Create a LogParser + score::mw::log::INvConfigMock nvconfig_mock; + score::platform::internal::LogParser parser(nvconfig_mock); + + // Call the static helper function - this covers the parser callback lambda body (lines 192-194) + SocketServer::UpdateParserHandlers(*dlt_server, parser, true); + SocketServer::UpdateParserHandlers(*dlt_server, parser, false); + + // If we reach here without crashing, the function executed successfully + SUCCEED(); +} + +TEST_F(SocketServerRemainingFunctionsTest, UpdateHandlersFinalExecutesSuccessfully) +{ + RecordProperty("Description", "Verify UpdateHandlersFinal static function works correctly"); + RecordProperty("TestType", "Interface test"); + RecordProperty("Verifies", "::score::platform::datarouter::SocketServer::UpdateHandlersFinal()"); + RecordProperty("DerivationTechnique", "Error guessing based on knowledge or experience"); + + // Create DltLogServer + auto dlt_server = SocketServer::CreateDltServer(storage_handlers_); + ASSERT_NE(nullptr, dlt_server); + + // Call the static helper function - this covers the final callback lambda body (lines 195-197) + SocketServer::UpdateHandlersFinal(*dlt_server, true); + SocketServer::UpdateHandlersFinal(*dlt_server, false); + + // If we reach here without crashing, the function executed successfully + SUCCEED(); +} + +TEST_F(SocketServerRemainingFunctionsTest, CreateConfigSessionExecutesSuccessfully) +{ + RecordProperty("Description", "Verify CreateConfigSession static function works correctly"); + RecordProperty("TestType", "Interface test"); + RecordProperty("Verifies", "::score::platform::datarouter::SocketServer::CreateConfigSession()"); + RecordProperty("DerivationTechnique", "Error guessing based on knowledge or experience"); + + // Create DltLogServer + auto dlt_server = SocketServer::CreateDltServer(storage_handlers_); + ASSERT_NE(nullptr, dlt_server); + + // Create a SessionHandle with a valid file descriptor + // Using pipe() to create a valid fd for testing + int pipe_fds[2]; + ASSERT_EQ(0, ::pipe(pipe_fds)); + + UnixDomainServer::SessionHandle handle(pipe_fds[0]); + + // Call the static helper function - this covers the factory lambda body (lines 205-206) + auto session = SocketServer::CreateConfigSession(*dlt_server, std::move(handle)); + + // Verify that a session was created + EXPECT_NE(nullptr, session); + + // Clean up + ::close(pipe_fds[1]); +} + +TEST_F(SocketServerRemainingFunctionsTest, CreateMessagePassingSessionErrorPath) +{ + RecordProperty("Description", "Verify CreateMessagePassingSession handles file open error correctly"); + RecordProperty("TestType", "Fault injection test"); + RecordProperty("Verifies", "::score::platform::datarouter::SocketServer::CreateMessagePassingSession()"); + RecordProperty("DerivationTechnique", "Error guessing based on knowledge or experience"); + RecordProperty("InjectionPoints", "Non-existent shared memory file"); + RecordProperty("MeasurementPoints", "Function returns nullptr"); + + // Create DltLogServer + auto dlt_server = SocketServer::CreateDltServer(storage_handlers_); + ASSERT_NE(nullptr, dlt_server); + + // Create DataRouter + score::mw::log::Logger& logger = score::mw::log::CreateLogger("TEST", "test"); + const auto source_setup = SocketServer::CreateSourceSetupHandler(*dlt_server); + DataRouter router(logger, source_setup); + + // Load NvConfig + const auto nv_config = SocketServer::LoadNvConfig(logger, test_config_path_); + + // Create a ConnectMessageFromClient - this will try to open a non-existent file + // The error path should return nullptr + score::mw::log::detail::ConnectMessageFromClient conn; + // Note: CreateMessagePassingSession will fail because the shared memory file doesn't exist + // This tests the error handling path (file open fails) + + auto session = SocketServer::CreateMessagePassingSession(router, *dlt_server, nv_config, 12345, conn, nullptr); + + // Verify that nullptr is returned when file doesn't exist (error path) + EXPECT_EQ(nullptr, session); +} + +TEST_F(SocketServerRemainingFunctionsTest, CreateMessagePassingSessionSuccessPath) +{ + RecordProperty("Description", "Verify CreateMessagePassingSession creates session when file exists"); + RecordProperty("TestType", "Interface test"); + RecordProperty("Verifies", "::score::platform::datarouter::SocketServer::CreateMessagePassingSession()"); + RecordProperty("DerivationTechnique", "Error guessing based on knowledge or experience"); + + // Create DltLogServer + auto dlt_server = SocketServer::CreateDltServer(storage_handlers_); + ASSERT_NE(nullptr, dlt_server); + + // Create DataRouter + score::mw::log::Logger& logger = score::mw::log::CreateLogger("TEST", "test"); + const auto source_setup = SocketServer::CreateSourceSetupHandler(*dlt_server); + DataRouter router(logger, source_setup); + + // Load NvConfig + const auto nv_config = SocketServer::LoadNvConfig(logger, test_config_path_); + + // Create a temporary file to simulate shared memory file + const std::string test_shmem_file = "/tmp/logging-test12.shmem"; + std::ofstream temp_file(test_shmem_file); + temp_file << "test data"; + temp_file.close(); + + // Create a ConnectMessageFromClient that will use the test file + score::mw::log::detail::ConnectMessageFromClient conn; + conn.SetUseDynamicIdentifier(true); + std::array random_part = {'t', 'e', 's', 't', '1', '2'}; + conn.SetRandomPart(random_part); + + // Call CreateMessagePassingSession - executes success path (file exists and opens) + // Note: session can still be nullptr if shared memory data is invalid, but the + // success path code (lines 279-295) will execute + auto session = SocketServer::CreateMessagePassingSession(router, *dlt_server, nv_config, 12345, conn, nullptr); + + // Success path executed - session may be null if data is invalid, that's okay for coverage + // The goal is to execute lines 279-295, not to validate the session outcome + SUCCEED(); // If we got here, success path was executed + + // Clean up the test file + std::remove(test_shmem_file.c_str()); +} + +TEST_F(SocketServerRemainingFunctionsTest, CreateMessagePassingSessionCloseFailure) +{ + RecordProperty("Description", "Verify CreateMessagePassingSession handles close() failure correctly"); + RecordProperty("TestType", "Fault injection test"); + RecordProperty("Verifies", "::score::platform::datarouter::SocketServer::CreateMessagePassingSession()"); + RecordProperty("DerivationTechnique", "Error guessing based on knowledge or experience"); + RecordProperty("InjectionPoints", "File close() system call failure"); + RecordProperty("MeasurementPoints", "Function continues execution despite close failure"); + + // Create DltLogServer + auto dlt_server = SocketServer::CreateDltServer(storage_handlers_); + ASSERT_NE(nullptr, dlt_server); + + // Create DataRouter + score::mw::log::Logger& logger = score::mw::log::CreateLogger("TEST", "test"); + const auto source_setup = SocketServer::CreateSourceSetupHandler(*dlt_server); + DataRouter router(logger, source_setup); + + // Load NvConfig + const auto nv_config = SocketServer::LoadNvConfig(logger, test_config_path_); + + // Create a temporary shared memory file + const std::string test_shmem_file = "/tmp/logging-test99.shmem"; + std::ofstream temp_file(test_shmem_file); + temp_file << "test data for close failure"; + temp_file.close(); + + // Create a ConnectMessageFromClient + score::mw::log::detail::ConnectMessageFromClient conn; + conn.SetUseDynamicIdentifier(true); + std::array random_part = {'t', 'e', 's', 't', '9', '9'}; + conn.SetRandomPart(random_part); + + // Mock Unistd to make close() fail + ::score::os::MockGuard unistd_mock; + + // Expect close to be called and return an error + EXPECT_CALL(*unistd_mock, close(_)) + .WillOnce(Return(score::cpp::unexpected(score::os::Error::createFromErrno(EBADF)))); + + // Call CreateMessagePassingSession - close will fail but function should handle it + auto session = SocketServer::CreateMessagePassingSession(router, *dlt_server, nv_config, 12345, conn, nullptr); + + // The close error is logged but doesn't prevent function completion + // Session may still be null due to invalid shared memory data, but that's okay + SUCCEED(); // If we got here, close error path was executed + + // Clean up the test file + std::remove(test_shmem_file.c_str()); +} + +} // namespace +} // namespace datarouter +} // namespace platform +} // namespace score diff --git a/score/mw/log/detail/data_router/shared_memory/BUILD b/score/mw/log/detail/data_router/shared_memory/BUILD index d59be1d..c337381 100644 --- a/score/mw/log/detail/data_router/shared_memory/BUILD +++ b/score/mw/log/detail/data_router/shared_memory/BUILD @@ -71,6 +71,7 @@ cc_library( "shared_memory_reader.cpp", ], hdrs = [ + "i_shared_memory_reader.h", "reader_factory.h", "reader_factory_impl.h", "shared_memory_reader.h", @@ -104,6 +105,16 @@ cc_library( deps = ["@googletest//:gtest"], ) +cc_library( + name = "shared_memory_reader_mock", + testonly = True, + srcs = [ + "shared_memory_reader_mock.h", + ], + visibility = ["//score/datarouter/test:__subpackages__"], + deps = ["@googletest//:gtest"], +) + cc_test( name = "unit_test", srcs = [ diff --git a/score/mw/log/detail/data_router/shared_memory/common.h b/score/mw/log/detail/data_router/shared_memory/common.h index 04edb1c..0bccd2d 100644 --- a/score/mw/log/detail/data_router/shared_memory/common.h +++ b/score/mw/log/detail/data_router/shared_memory/common.h @@ -11,8 +11,8 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -#ifndef BMW_MW_LOG_WAIT_FREE_SHARED_MEMORY_COMMON -#define BMW_MW_LOG_WAIT_FREE_SHARED_MEMORY_COMMON +#ifndef BMW_MW_LOG_SHARED_MEMORY_COMMON +#define BMW_MW_LOG_SHARED_MEMORY_COMMON #include "score/os/utils/high_resolution_steady_clock.h" #include "score/mw/log/detail/wait_free_producer_queue/alternating_control_block.h" @@ -100,4 +100,4 @@ constexpr TypeIdentifier GetRegisterTypeToken() } // namespace mw } // namespace score -#endif // BMW_MW_LOG_WAIT_FREE_SHARED_MEMORY_COMMON +#endif // BMW_MW_LOG_SHARED_MEMORY_COMMON diff --git a/score/mw/log/detail/data_router/shared_memory/i_shared_memory_reader.h b/score/mw/log/detail/data_router/shared_memory/i_shared_memory_reader.h new file mode 100644 index 0000000..e387c44 --- /dev/null +++ b/score/mw/log/detail/data_router/shared_memory/i_shared_memory_reader.h @@ -0,0 +1,78 @@ +#ifndef BMW_MW_LOG_WAIT_FREE_I_SHARED_MEMORY_READER +#define BMW_MW_LOG_WAIT_FREE_I_SHARED_MEMORY_READER + +#include "score/mw/log/detail/data_router/shared_memory/common.h" +#include "score/mw/log/detail/wait_free_producer_queue/alternating_reader.h" + +namespace score +{ +namespace mw +{ +namespace log +{ +namespace detail +{ + +struct TypeRegistration +{ + /* + Maintaining compatibility and avoiding performance overhead outweighs POD Type (class) based design for this + particular struct. The Type is simple and does not require invariance (interface OR custom behavior) as per the + design. Moreover the type is ONLY used internally under the namespace detail and NOT exposed publicly; this is + additionally guaranteed by the build system(bazel) visibility + */ + // coverity[autosar_cpp14_m11_0_1_violation] + TypeIdentifier type_id{}; + // coverity[autosar_cpp14_m11_0_1_violation] + score::cpp::span registration_data; +}; + +using TypeRegistrationCallback = score::cpp::callback; + +struct SharedMemoryRecord +{ /* + Maintaining compatibility and avoiding performance overhead outweighs POD Type (class) based design for this + particular struct. The Type is simple and does not require invariance (interface OR custom behavior) as per the + design. Moreover the type is ONLY used internally under the namespace detail and NOT exposed publicly; this is + additionally guaranteed by the build system(bazel) visibility + */ + // coverity[autosar_cpp14_m11_0_1_violation] + BufferEntryHeader header; + // coverity[autosar_cpp14_m11_0_1_violation] + score::cpp::span payload; +}; + +using NewRecordCallback = score::cpp::callback; + +class ISharedMemoryReader +{ + public: + virtual ~ISharedMemoryReader() = default; + + virtual std::optional Read(const TypeRegistrationCallback& type_registration_callback, + const NewRecordCallback& new_message_callback) noexcept = 0; + + virtual std::optional PeekNumberOfBytesAcquiredInBuffer( + const std::uint32_t acquired_buffer_count_id) const noexcept = 0; + + virtual std::optional ReadDetached(const TypeRegistrationCallback& type_registration_callback, + const NewRecordCallback& new_message_callback) noexcept = 0; + + virtual Length GetNumberOfDropsWithBufferFull() const noexcept = 0; + virtual Length GetNumberOfDropsWithInvalidSize() const noexcept = 0; + virtual Length GetNumberOfDropsWithTypeRegistrationFailed() const noexcept = 0; + virtual Length GetSizeOfDropsWithBufferFull() const noexcept = 0; + + virtual Length GetRingBufferSizeBytes() const noexcept = 0; + + virtual bool IsBlockReleasedByWriters(const std::uint32_t block_count) noexcept = 0; + + virtual std::optional NotifyAcquisitionSetReader(const ReadAcquireResult& acquire_result) noexcept = 0; +}; + +} // namespace detail +} // namespace log +} // namespace mw +} // namespace score + +#endif // BMW_MW_LOG_WAIT_FREE_I_SHARED_MEMORY_READER diff --git a/score/mw/log/detail/data_router/shared_memory/reader_factory.h b/score/mw/log/detail/data_router/shared_memory/reader_factory.h index 361d218..107dd58 100644 --- a/score/mw/log/detail/data_router/shared_memory/reader_factory.h +++ b/score/mw/log/detail/data_router/shared_memory/reader_factory.h @@ -11,8 +11,8 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -#ifndef BMW_MW_LOG_WAIT_FREE_SHARED_MEMORY_READER_FACTORY -#define BMW_MW_LOG_WAIT_FREE_SHARED_MEMORY_READER_FACTORY +#ifndef BMW_MW_LOG_SHARED_MEMORY_READER_FACTORY +#define BMW_MW_LOG_SHARED_MEMORY_READER_FACTORY #include "score/mw/log/detail/data_router/shared_memory/shared_memory_reader.h" @@ -36,7 +36,8 @@ using ReaderFactoryPtr = std::unique_ptr; class ReaderFactory { public: - virtual std::optional Create(const std::int32_t file_descriptor, const pid_t expected_pid) = 0; + virtual std::unique_ptr Create(const std::int32_t file_descriptor, + const pid_t expected_pid) = 0; ReaderFactory() = default; virtual ~ReaderFactory() = default; @@ -53,4 +54,4 @@ class ReaderFactory } // namespace mw } // namespace score -#endif // BMW_MW_LOG_WAIT_FREE_SHARED_MEMORY_READER_FACTORY +#endif // BMW_MW_LOG_SHARED_MEMORY_READER_FACTORY diff --git a/score/mw/log/detail/data_router/shared_memory/reader_factory_impl.cpp b/score/mw/log/detail/data_router/shared_memory/reader_factory_impl.cpp index 9a932ae..4cb16b9 100644 --- a/score/mw/log/detail/data_router/shared_memory/reader_factory_impl.cpp +++ b/score/mw/log/detail/data_router/shared_memory/reader_factory_impl.cpp @@ -37,8 +37,8 @@ Byte* GetBufferAddress(Byte* const start, const Length offset) return start_address; } -std::optional ReaderFactoryImpl::Create(const std::int32_t file_descriptor, - const pid_t expected_pid) noexcept +std::unique_ptr ReaderFactoryImpl::Create(const std::int32_t file_descriptor, + const pid_t expected_pid) noexcept { score::os::StatBuffer buffer{}; @@ -47,13 +47,13 @@ std::optional ReaderFactoryImpl::Create(const std::int32_t f if (stat_result.has_value() == false) { std::cerr << "ReaderFactoryImpl::Create: fstat failed: " << stat_result.error(); - return {}; + return nullptr; } if (buffer.st_size < 0) { std::cerr << "ReaderFactoryImpl::Create: unexpected negative buffer.st_size: " << buffer.st_size; - return {}; + return nullptr; } const auto map_size_bytes = static_cast(buffer.st_size); @@ -62,7 +62,7 @@ std::optional ReaderFactoryImpl::Create(const std::int32_t f { std::cerr << "ReaderFactoryImpl::Create: Invalid shared memory size: found " << map_size_bytes << " but expected at least " << sizeof(SharedData) << " bytes\n"; - return {}; + return nullptr; } static constexpr void* null_addr = nullptr; @@ -77,7 +77,7 @@ std::optional ReaderFactoryImpl::Create(const std::int32_t f if (mmap_result.has_value() == false) { std::cerr << "ReaderFactoryImpl::Create: mmap failed: " << mmap_result.error() << '\n'; - return {}; + return nullptr; } /* @@ -108,7 +108,7 @@ std::optional ReaderFactoryImpl::Create(const std::int32_t f std::cerr << "ReaderFactoryImpl::Create: Invalid shared_data content: max_offset_bytes=" << max_offset_bytes << " but map_size_bytes is only " << map_size_bytes << '\n'; unmap_callback(); - return {}; + return nullptr; } if (shared_data.producer_pid != expected_pid) @@ -116,7 +116,7 @@ std::optional ReaderFactoryImpl::Create(const std::int32_t f std::cerr << "SharedMemoryReader found invalid pid. Expected " << expected_pid << " but found " << shared_data.producer_pid << " in shared memory. Dropping the logs from this client.\n"; unmap_callback(); - return {}; + return nullptr; } /* Deviation from Rule M5-2-8: @@ -135,7 +135,8 @@ std::optional ReaderFactoryImpl::Create(const std::int32_t f AlternatingReadOnlyReader alternating_read_only_reader{ shared_data.control_block, buffer_block_even, buffer_block_odd}; - return SharedMemoryReader(shared_data, std::move(alternating_read_only_reader), std::move(unmap_callback)); + return std::make_unique( + shared_data, std::move(alternating_read_only_reader), std::move(unmap_callback)); } ReaderFactoryPtr ReaderFactory::Default(score::cpp::pmr::memory_resource* memory_resource) noexcept diff --git a/score/mw/log/detail/data_router/shared_memory/reader_factory_impl.h b/score/mw/log/detail/data_router/shared_memory/reader_factory_impl.h index 0fb4379..bcc6c47 100644 --- a/score/mw/log/detail/data_router/shared_memory/reader_factory_impl.h +++ b/score/mw/log/detail/data_router/shared_memory/reader_factory_impl.h @@ -11,8 +11,8 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -#ifndef BMW_MW_LOG_WAIT_FREE_SHARED_MEMORY_READER_FACTORY_IMPL -#define BMW_MW_LOG_WAIT_FREE_SHARED_MEMORY_READER_FACTORY_IMPL +#ifndef BMW_MW_LOG_SHARED_MEMORY_READER_FACTORY_IMPL +#define BMW_MW_LOG_SHARED_MEMORY_READER_FACTORY_IMPL #include "score/mw/log/detail/data_router/shared_memory/reader_factory.h" @@ -36,8 +36,8 @@ class ReaderFactoryImpl : public ReaderFactory public: explicit ReaderFactoryImpl(score::cpp::pmr::unique_ptr&& mman, score::cpp::pmr::unique_ptr&& stat_osal) noexcept; - std::optional Create(const std::int32_t file_descriptor, - const pid_t expected_pid) noexcept override; + std::unique_ptr Create(const std::int32_t file_descriptor, + const pid_t expected_pid) noexcept override; private: score::cpp::pmr::unique_ptr mman_; @@ -49,4 +49,4 @@ class ReaderFactoryImpl : public ReaderFactory } // namespace mw } // namespace score -#endif // BMW_MW_LOG_WAIT_FREE_SHARED_MEMORY_READER_FACTORY_IMPL +#endif // BMW_MW_LOG_SHARED_MEMORY_READER_FACTORY_IMPL diff --git a/score/mw/log/detail/data_router/shared_memory/reader_factory_mock.h b/score/mw/log/detail/data_router/shared_memory/reader_factory_mock.h index 3866e68..4f824b1 100644 --- a/score/mw/log/detail/data_router/shared_memory/reader_factory_mock.h +++ b/score/mw/log/detail/data_router/shared_memory/reader_factory_mock.h @@ -11,8 +11,8 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -#ifndef BMW_MW_LOG_WAIT_FREE_SHARED_MEMORY_READER_FACTORY_MOCK -#define BMW_MW_LOG_WAIT_FREE_SHARED_MEMORY_READER_FACTORY_MOCK +#ifndef BMW_MW_LOG_SHARED_MEMORY_READER_FACTORY_MOCK +#define BMW_MW_LOG_SHARED_MEMORY_READER_FACTORY_MOCK #include "score/mw/log/detail/data_router/shared_memory/reader_factory.h" @@ -31,7 +31,7 @@ namespace detail class ReaderFactoryMock : public ReaderFactory { public: - MOCK_METHOD((std::optional), + MOCK_METHOD((std::unique_ptr), Create, (const std::int32_t file_handle, const pid_t expected_pid), (override)); @@ -42,4 +42,4 @@ class ReaderFactoryMock : public ReaderFactory } // namespace mw } // namespace score -#endif // BMW_MW_LOG_WAIT_FREE_SHARED_MEMORY_READER_FACTORY_MOCK +#endif // BMW_MW_LOG_SHARED_MEMORY_READER_FACTORY_MOCK diff --git a/score/mw/log/detail/data_router/shared_memory/reader_factory_test.cpp b/score/mw/log/detail/data_router/shared_memory/reader_factory_test.cpp index 7977fed..9924707 100644 --- a/score/mw/log/detail/data_router/shared_memory/reader_factory_test.cpp +++ b/score/mw/log/detail/data_router/shared_memory/reader_factory_test.cpp @@ -83,7 +83,7 @@ TEST_F(ReaderFactoryFixture, FailingCallToFstatShallResultInEmptyOptional) EXPECT_CALL(*mman_mock_, mmap(_, _, _, _, _, _)).Times(0); auto result = factory_.Create(kFileHandle, kExpectedPid); - EXPECT_FALSE(result.has_value()); + EXPECT_EQ(result, nullptr); } TEST_F(ReaderFactoryFixture, FstatInvalidReturnShallResultInEmptyOptional) @@ -104,7 +104,7 @@ TEST_F(ReaderFactoryFixture, FstatInvalidReturnShallResultInEmptyOptional) EXPECT_CALL(*mman_mock_, mmap(_, _, _, _, _, _)).Times(0); auto result = factory_.Create(kFileHandle, kExpectedPid); - EXPECT_FALSE(result.has_value()); + EXPECT_FALSE(result); } TEST_F(ReaderFactoryFixture, FstatReturningSizeTooSmallShallResultInEmptyOptional) @@ -127,7 +127,7 @@ TEST_F(ReaderFactoryFixture, FstatReturningSizeTooSmallShallResultInEmptyOptiona EXPECT_CALL(*mman_mock_, mmap(_, _, _, _, _, _)).Times(0); auto result = factory_.Create(kFileHandle, kExpectedPid); - EXPECT_FALSE(result.has_value()); + EXPECT_EQ(result, nullptr); } TEST_F(ReaderFactoryFixture, MmapFailingShallResultInEmptyOptional) @@ -154,7 +154,7 @@ TEST_F(ReaderFactoryFixture, MmapFailingShallResultInEmptyOptional) .WillOnce(Return(score::cpp::make_unexpected(score::os::Error::createFromErrno(EINVAL)))); auto result = factory_.Create(kFileHandle, kExpectedPid); - EXPECT_FALSE(result.has_value()); + EXPECT_EQ(result, nullptr); } TEST_F(ReaderFactoryFixture, SharedDataMemberPointingOutOfBoundsShallResultInEmptyOptional) @@ -187,7 +187,7 @@ TEST_F(ReaderFactoryFixture, SharedDataMemberPointingOutOfBoundsShallResultInEmp EXPECT_CALL(*mman_mock_, munmap(_, kSharedSize)).WillOnce(Return(score::cpp::expected_blank{})); auto result = factory_.Create(kFileHandle, kExpectedPid); - EXPECT_FALSE(result.has_value()); + EXPECT_EQ(result, nullptr); } TEST_F(ReaderFactoryFixture, UnexpectedPidShallResultInEmptyOptional) @@ -218,7 +218,7 @@ TEST_F(ReaderFactoryFixture, UnexpectedPidShallResultInEmptyOptional) EXPECT_CALL(*mman_mock_, munmap(_, kSharedSize)).WillOnce(Return(score::cpp::expected_blank{})); auto result = factory_.Create(kFileHandle, kExpectedPid); - EXPECT_FALSE(result.has_value()); + EXPECT_EQ(result, nullptr); } TEST_F(ReaderFactoryFixture, ProperSetupShallResultValidReader) @@ -248,7 +248,7 @@ TEST_F(ReaderFactoryFixture, ProperSetupShallResultValidReader) EXPECT_CALL(*mman_mock_, munmap(_, kSharedSize)).Times(0); auto result = factory_.Create(kFileHandle, kExpectedPid); - EXPECT_TRUE(result.has_value()); + EXPECT_NE(result, nullptr); EXPECT_CALL(*mman_mock_, munmap(_, kSharedSize)).WillOnce(Return(score::cpp::expected_blank{})); } @@ -277,7 +277,7 @@ TEST_F(ReaderFactoryFixture, UnmapFailureShallResultValidReader) .WillOnce(Return(score::cpp::expected{&buffer_})); auto result = factory_.Create(kFileHandle, kExpectedPid); - EXPECT_TRUE(result.has_value()); + EXPECT_NE(result, nullptr); EXPECT_CALL(*mman_mock_, munmap(_, kSharedSize)) .WillOnce(Return(score::cpp::make_unexpected(score::os::Error::createFromErrno(EINVAL)))); diff --git a/score/mw/log/detail/data_router/shared_memory/shared_memory_reader.h b/score/mw/log/detail/data_router/shared_memory/shared_memory_reader.h index 017380e..6282a2e 100644 --- a/score/mw/log/detail/data_router/shared_memory/shared_memory_reader.h +++ b/score/mw/log/detail/data_router/shared_memory/shared_memory_reader.h @@ -11,10 +11,10 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -#ifndef BMW_MW_LOG_WAIT_FREE_SHARED_MEMORY_READER -#define BMW_MW_LOG_WAIT_FREE_SHARED_MEMORY_READER +#ifndef BMW_MW_LOG_SHARED_MEMORY_READER +#define BMW_MW_LOG_SHARED_MEMORY_READER -#include "score/mw/log/detail/data_router/shared_memory/common.h" +#include "score/mw/log/detail/data_router/shared_memory/i_shared_memory_reader.h" #include "score/mw/log/detail/wait_free_producer_queue/alternating_reader.h" #include @@ -30,40 +30,9 @@ namespace log namespace detail { -struct TypeRegistration -{ - /* - Maintaining compatibility and avoiding performance overhead outweighs POD Type (class) based design for this - particular struct. The Type is simple and does not require invariance (interface OR custom behavior) as per the - design. Moreover the type is ONLY used internally under the namespace detail and NOT exposed publicly; this is - additionally guaranteed by the build system(bazel) visibility - */ - // coverity[autosar_cpp14_m11_0_1_violation] - TypeIdentifier type_id{}; - // coverity[autosar_cpp14_m11_0_1_violation] - score::cpp::span registration_data; -}; - -using TypeRegistrationCallback = score::cpp::callback; - -struct SharedMemoryRecord -{ /* - Maintaining compatibility and avoiding performance overhead outweighs POD Type (class) based design for this - particular struct. The Type is simple and does not require invariance (interface OR custom behavior) as per the - design. Moreover the type is ONLY used internally under the namespace detail and NOT exposed publicly; this is - additionally guaranteed by the build system(bazel) visibility - */ - // coverity[autosar_cpp14_m11_0_1_violation] - BufferEntryHeader header; - // coverity[autosar_cpp14_m11_0_1_violation] - score::cpp::span payload; -}; - -using NewRecordCallback = score::cpp::callback; - /// \brief This class manages the reading of serialized data types on read-only shared memory. /// This class is not thread safe. -class SharedMemoryReader +class SharedMemoryReader : public ISharedMemoryReader { public: explicit SharedMemoryReader(const SharedData& shared_data, @@ -82,29 +51,29 @@ class SharedMemoryReader /// writers based on the assumption that the writer has already finished any activities leading to data /// modification. In this case it is assumed that logging client has terminated or crashed. std::optional Read(const TypeRegistrationCallback& type_registration_callback, - const NewRecordCallback& new_message_callback) noexcept; + const NewRecordCallback& new_message_callback) noexcept override; // This function may be used to get a temporary view of the value of bytes acquired by writers. std::optional PeekNumberOfBytesAcquiredInBuffer( - const std::uint32_t acquired_buffer_count_id) const noexcept; + const std::uint32_t acquired_buffer_count_id) const noexcept override; /// \brief Method shall be called when a client closed the connection to Datarouter. std::optional ReadDetached(const TypeRegistrationCallback& type_registration_callback, - const NewRecordCallback& new_message_callback) noexcept; + const NewRecordCallback& new_message_callback) noexcept override; - Length GetNumberOfDropsWithBufferFull() const noexcept; - Length GetNumberOfDropsWithInvalidSize() const noexcept; - Length GetNumberOfDropsWithTypeRegistrationFailed() const noexcept; - Length GetSizeOfDropsWithBufferFull() const noexcept; + Length GetNumberOfDropsWithBufferFull() const noexcept override; + Length GetNumberOfDropsWithInvalidSize() const noexcept override; + Length GetNumberOfDropsWithTypeRegistrationFailed() const noexcept override; + Length GetSizeOfDropsWithBufferFull() const noexcept override; - Length GetRingBufferSizeBytes() const noexcept; + Length GetRingBufferSizeBytes() const noexcept override; - bool IsBlockReleasedByWriters(const std::uint32_t block_count) noexcept; + bool IsBlockReleasedByWriters(const std::uint32_t block_count) noexcept override; /// \brief This method shall be called by the server when a client has acknowledged an acquire request. /// It sets Reader to acquired data that can be later used by Read() method /// Returns number of bytes of acquired buffer if available. Otherwise it returns std::nullopt - std::optional NotifyAcquisitionSetReader(const ReadAcquireResult& acquire_result) noexcept; + std::optional NotifyAcquisitionSetReader(const ReadAcquireResult& acquire_result) noexcept override; private: const SharedData& shared_data_; @@ -129,4 +98,4 @@ class SharedMemoryReader } // namespace mw } // namespace score -#endif // MWSR_WRITER_IMPL_H_ +#endif // BMW_MW_LOG_SHARED_MEMORY_READER diff --git a/score/mw/log/detail/data_router/shared_memory/shared_memory_reader_mock.h b/score/mw/log/detail/data_router/shared_memory/shared_memory_reader_mock.h new file mode 100644 index 0000000..242422d --- /dev/null +++ b/score/mw/log/detail/data_router/shared_memory/shared_memory_reader_mock.h @@ -0,0 +1,55 @@ +#ifndef BMW_MW_LOG_SHARED_MEMORY_READER_MOCK +#define BMW_MW_LOG_SHARED_MEMORY_READER_MOCK + +#include "score/mw/log/detail/data_router/shared_memory/i_shared_memory_reader.h" + +#include + +namespace score +{ +namespace mw +{ +namespace log +{ +namespace detail +{ + +/// \brief The factory is responsible for creating the shared memory file and instantiating the SharedMemoryReader +class ISharedMemoryReaderMock : public ISharedMemoryReader +{ + public: + MOCK_METHOD(std::optional, + Read, + (const TypeRegistrationCallback& type_registration_callback, + const NewRecordCallback& new_message_callback), + (noexcept, override)); + + MOCK_METHOD(std::optional, + PeekNumberOfBytesAcquiredInBuffer, + (const std::uint32_t acquired_buffer_count_id), + (const, noexcept, override)); + + MOCK_METHOD(std::optional, + ReadDetached, + (const TypeRegistrationCallback& type_registration_callback, + const NewRecordCallback& new_message_callback), + (noexcept, override)); + + MOCK_METHOD(Length, GetNumberOfDropsWithBufferFull, (), (const, noexcept, override)); + MOCK_METHOD(Length, GetNumberOfDropsWithInvalidSize, (), (const, noexcept, override)); + MOCK_METHOD(Length, GetNumberOfDropsWithTypeRegistrationFailed, (), (const, noexcept, override)); + MOCK_METHOD(Length, GetSizeOfDropsWithBufferFull, (), (const, noexcept, override)); + MOCK_METHOD(Length, GetRingBufferSizeBytes, (), (const, noexcept, override)); + MOCK_METHOD(bool, IsBlockReleasedByWriters, (const std::uint32_t block_count), (noexcept, override)); + MOCK_METHOD(std::optional, + NotifyAcquisitionSetReader, + (const ReadAcquireResult& acquire_result), + (noexcept, override)); +}; + +} // namespace detail +} // namespace log +} // namespace mw +} // namespace score + +#endif // BMW_MW_LOG_SHARED_MEMORY_READER_MOCK diff --git a/score/mw/log/detail/data_router/shared_memory/shared_memory_writer.h b/score/mw/log/detail/data_router/shared_memory/shared_memory_writer.h index c18b5ee..347ded2 100644 --- a/score/mw/log/detail/data_router/shared_memory/shared_memory_writer.h +++ b/score/mw/log/detail/data_router/shared_memory/shared_memory_writer.h @@ -11,8 +11,8 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -#ifndef BMW_MW_LOG_WAIT_FREE_SHARED_MEMORY_WRITER -#define BMW_MW_LOG_WAIT_FREE_SHARED_MEMORY_WRITER +#ifndef BMW_MW_LOG_SHARED_MEMORY_WRITER +#define BMW_MW_LOG_SHARED_MEMORY_WRITER #include "score/mw/log/detail/data_router/shared_memory/common.h" #include "score/mw/log/detail/wait_free_producer_queue/alternating_reader_proxy.h" @@ -207,4 +207,4 @@ class SharedMemoryWriter } // namespace mw } // namespace score -#endif // BMW_MW_LOG_WAIT_FREE_SHARED_MEMORY_READER +#endif // BMW_MW_LOG_SHARED_MEMORY_WRITER diff --git a/score/mw/log/detail/data_router/shared_memory/writer_factory.h b/score/mw/log/detail/data_router/shared_memory/writer_factory.h index ab11a10..1fcc1ed 100644 --- a/score/mw/log/detail/data_router/shared_memory/writer_factory.h +++ b/score/mw/log/detail/data_router/shared_memory/writer_factory.h @@ -11,8 +11,8 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -#ifndef BMW_MW_LOG_WAIT_FREE_SHARED_MEMORY_WRITER_FACTORY -#define BMW_MW_LOG_WAIT_FREE_SHARED_MEMORY_WRITER_FACTORY +#ifndef BMW_MW_LOG_SHARED_MEMORY_WRITER_FACTORY +#define BMW_MW_LOG_SHARED_MEMORY_WRITER_FACTORY #include "score/mw/log/detail/data_router/shared_memory/shared_memory_writer.h" From fe00cc7dc9911ea3a9a8aa21b392a6c0fa4480ca Mon Sep 17 00:00:00 2001 From: Raghavendra Maddikery Date: Tue, 20 Jan 2026 07:43:19 +0100 Subject: [PATCH 04/34] Skip test if feature is disabled --- score/datarouter/test/ut/ut_logging/test_socketserver.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/score/datarouter/test/ut/ut_logging/test_socketserver.cpp b/score/datarouter/test/ut/ut_logging/test_socketserver.cpp index 9048f74..496e4d7 100644 --- a/score/datarouter/test/ut/ut_logging/test_socketserver.cpp +++ b/score/datarouter/test/ut/ut_logging/test_socketserver.cpp @@ -4,6 +4,7 @@ #include "score/mw/log/configuration/invconfig_mock.h" #include "score/datarouter/datarouter/data_router.h" #include "score/datarouter/src/persistency/mock_persistent_dictionary.h" +#include "applications/datarouter_feature_config.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -284,6 +285,11 @@ TEST_F(SocketServerRemainingFunctionsTest, LoadNvConfigSuccessPath) RecordProperty("Verifies", "::score::platform::datarouter::SocketServer::LoadNvConfig()"); RecordProperty("DerivationTechnique", "Error guessing based on knowledge or experience"); + if constexpr (!score::platform::datarouter::kNonVerboseDltEnabled) + { + GTEST_SKIP() << "Test requires NON_VERBOSE_DLT feature to be enabled"; + } + score::mw::log::Logger& logger = score::mw::log::CreateLogger("TEST", "test"); // Call LoadNvConfig with valid test data - should succeed (lines 225-226) From cf6e46d74738c90adcb0000ba1543d026414c234 Mon Sep 17 00:00:00 2001 From: Raghavendra Maddikery Date: Tue, 20 Jan 2026 13:40:48 +0100 Subject: [PATCH 05/34] Adds missing copyright headers --- score/datarouter/mocks/logparser_mock.h | 13 +++++++++++++ .../test/ut/ut_logging/test_socketserver.cpp | 13 +++++++++++++ .../shared_memory/i_shared_memory_reader.h | 13 +++++++++++++ .../shared_memory/shared_memory_reader_mock.h | 13 +++++++++++++ 4 files changed, 52 insertions(+) diff --git a/score/datarouter/mocks/logparser_mock.h b/score/datarouter/mocks/logparser_mock.h index f38c5f0..346deff 100644 --- a/score/datarouter/mocks/logparser_mock.h +++ b/score/datarouter/mocks/logparser_mock.h @@ -1,3 +1,16 @@ +/******************************************************************************** + * 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 PAS_LOGGING_LOGPARSERMOCK_H_ #define PAS_LOGGING_LOGPARSERMOCK_H_ diff --git a/score/datarouter/test/ut/ut_logging/test_socketserver.cpp b/score/datarouter/test/ut/ut_logging/test_socketserver.cpp index 496e4d7..e7af5bd 100644 --- a/score/datarouter/test/ut/ut_logging/test_socketserver.cpp +++ b/score/datarouter/test/ut/ut_logging/test_socketserver.cpp @@ -1,3 +1,16 @@ +/******************************************************************************** + * 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 "daemon/socketserver.h" #include "logparser/logparser.h" #include "score/os/mocklib/unistdmock.h" diff --git a/score/mw/log/detail/data_router/shared_memory/i_shared_memory_reader.h b/score/mw/log/detail/data_router/shared_memory/i_shared_memory_reader.h index e387c44..39fe5a7 100644 --- a/score/mw/log/detail/data_router/shared_memory/i_shared_memory_reader.h +++ b/score/mw/log/detail/data_router/shared_memory/i_shared_memory_reader.h @@ -1,3 +1,16 @@ +/******************************************************************************** + * 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 BMW_MW_LOG_WAIT_FREE_I_SHARED_MEMORY_READER #define BMW_MW_LOG_WAIT_FREE_I_SHARED_MEMORY_READER diff --git a/score/mw/log/detail/data_router/shared_memory/shared_memory_reader_mock.h b/score/mw/log/detail/data_router/shared_memory/shared_memory_reader_mock.h index 242422d..4726e21 100644 --- a/score/mw/log/detail/data_router/shared_memory/shared_memory_reader_mock.h +++ b/score/mw/log/detail/data_router/shared_memory/shared_memory_reader_mock.h @@ -1,3 +1,16 @@ +/******************************************************************************** + * 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 BMW_MW_LOG_SHARED_MEMORY_READER_MOCK #define BMW_MW_LOG_SHARED_MEMORY_READER_MOCK From d1ddad56ab0b41881a080f48c87cb2a28a79ff40 Mon Sep 17 00:00:00 2001 From: Raghavendra Maddikery Date: Wed, 21 Jan 2026 12:44:39 +0100 Subject: [PATCH 06/34] Project import generated by Copybara. GIT_ORIGIN_SPP_REV_ID: 4519f8b22ed384c3b787c0c0c9d0060b3e57b30e --- .../test/ut/ut_logging/test_socketserver.cpp | 2 +- score/mw/log/BUILD | 22 ++++++++++++++++++- score/mw/log/detail/BUILD | 13 ++++++++++- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/score/datarouter/test/ut/ut_logging/test_socketserver.cpp b/score/datarouter/test/ut/ut_logging/test_socketserver.cpp index e7af5bd..cbf3097 100644 --- a/score/datarouter/test/ut/ut_logging/test_socketserver.cpp +++ b/score/datarouter/test/ut/ut_logging/test_socketserver.cpp @@ -11,13 +11,13 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ +#include "applications/datarouter_feature_config.h" #include "daemon/socketserver.h" #include "logparser/logparser.h" #include "score/os/mocklib/unistdmock.h" #include "score/mw/log/configuration/invconfig_mock.h" #include "score/datarouter/datarouter/data_router.h" #include "score/datarouter/src/persistency/mock_persistent_dictionary.h" -#include "applications/datarouter_feature_config.h" #include "gmock/gmock.h" #include "gtest/gtest.h" diff --git a/score/mw/log/BUILD b/score/mw/log/BUILD index 28b196c..7eb3436 100644 --- a/score/mw/log/BUILD +++ b/score/mw/log/BUILD @@ -1 +1,21 @@ -# Refer targets from score-baselibs# Refer targets from score-baselibs +# ******************************************************************************* +# 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 +# ******************************************************************************* + +cc_library( + name = "log", + visibility = ["//visibility:public"], + deps = [ + "//score/mw/log/detail/common:recorder_factory", + "@score_baselibs//score/mw/log:frontend", + ], +) diff --git a/score/mw/log/detail/BUILD b/score/mw/log/detail/BUILD index 28b196c..6bdeed2 100644 --- a/score/mw/log/detail/BUILD +++ b/score/mw/log/detail/BUILD @@ -1 +1,12 @@ -# Refer targets from score-baselibs# Refer targets from score-baselibs +# ******************************************************************************* +# 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 +# ******************************************************************************* From 0e14c9354466826b5db816b4563f31e2d1496597 Mon Sep 17 00:00:00 2001 From: Raghavendra Maddikery Date: Fri, 19 Dec 2025 09:48:09 +0100 Subject: [PATCH 07/34] Minor fixes to make modules buildable using bazel (#6) * Fix module name for baselibs nlohmann dep * Fix score_docs_as_code version based on baselibs * Fix typos in build configs. and documentation * Fixes ruff formatting in Python files * Fixes buildifier formatting in Starlark files --- MODULE.bazel | 2 +- score/datarouter/BUILD | 4 +- .../doc/guideline/file_based_local.md | 2 +- third_party/host_llvm/host_llvm.MODULE.bazel | 14 +-- .../tools/source_code_linker/get_git_info.py | 2 + .../source_code_linker/parse_source_files.py | 78 +++++++------ .../tools/trlc_renderer/trlc_renderer.py | 110 +++++++++++++----- 7 files changed, 138 insertions(+), 74 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index c2d7d07..990f071 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -17,6 +17,7 @@ module( ) bazel_dep(name = "platforms", version = "1.0.0") + bazel_dep(name = "score_toolchains_gcc", version = "0.4", dev_dependency = True) bazel_dep(name = "googletest", version = "1.17.0.bcr.1") @@ -89,7 +90,6 @@ download_archive( # C/C++ rules for Bazel bazel_dep(name = "rules_cc", version = "0.2.1") - bazel_dep(name = "nlohmann_json", version = "3.11.3") bazel_dep(name = "bazel_skylib", version = "1.7.1") bazel_dep(name = "rules_doxygen", version = "2.5.0") diff --git a/score/datarouter/BUILD b/score/datarouter/BUILD index d0ed939..14510e9 100644 --- a/score/datarouter/BUILD +++ b/score/datarouter/BUILD @@ -332,11 +332,11 @@ filegroup( "etc/persistent-logging.json", ], visibility = [ - "//ecu/xpad/xpad-shared/config/common/pas/datarouter:__subpackages__", + "//ecu/xyz/xyz-shared/config/common/pas/datarouter:__subpackages__", "//score/datarouter/test:__subpackages__", "//platform/aas/tools/itf:__subpackages__", "//platform/aas/tools/sctf:__subpackages__", - # "@ddad//ecu/xpad/xpad-shared/config/common/pas/datarouter:__subpackages__", + # "@ddad//ecu/xyz/xyz-shared/config/common/pas/datarouter:__subpackages__", ], ) diff --git a/score/datarouter/doc/guideline/file_based_local.md b/score/datarouter/doc/guideline/file_based_local.md index d9dce2a..2bf0855 100644 --- a/score/datarouter/doc/guideline/file_based_local.md +++ b/score/datarouter/doc/guideline/file_based_local.md @@ -2,7 +2,7 @@ You sometimes want to get a log in the filesystem. No matter if qemu or normal, this should work. Though you can set it up at application level only, it can bring significant benefits for offline analysis. -1. take a look at the following document regarding configuration of logging: [xpad doc](broken_link_g/xpad/documentation/blob/master/guidelines/logging/configuration.md) +1. take a look at the following document regarding configuration of logging: [xyz doc](broken_link_g/xyz/documentation/blob/master/guidelines/logging/configuration.md) The following config will set App1 to write into /tmp/App1.dlt diff --git a/third_party/host_llvm/host_llvm.MODULE.bazel b/third_party/host_llvm/host_llvm.MODULE.bazel index 59ccb7b..2f6a772 100644 --- a/third_party/host_llvm/host_llvm.MODULE.bazel +++ b/third_party/host_llvm/host_llvm.MODULE.bazel @@ -1,11 +1,7 @@ -bazel_dep(name = "toolchains_llvm", version = "1.5.0", dev_dependency=True) - -llvm = use_extension("@toolchains_llvm//toolchain/extensions:llvm.bzl", "llvm", dev_dependency=True) +bazel_dep(name = "toolchains_llvm", version = "1.5.0", dev_dependency = True) +llvm = use_extension("@toolchains_llvm//toolchain/extensions:llvm.bzl", "llvm", dev_dependency = True) llvm.toolchain( - llvm_version = "16.0.0", - cxx_standard = {"": "c++17"}, - stdlib ={"": "dynamic-stdc++"}, compile_flags = {"": [ "-march=nehalem", "-ffp-model=strict", @@ -19,9 +15,11 @@ llvm.toolchain( "-Wno-error=self-assign-overloaded", "-Wthread-safety", ]}, - link_libs ={"":[ + cxx_standard = {"": "c++17"}, + link_libs = {"": [ "-lrt", ]}, + llvm_version = "16.0.0", + stdlib = {"": "dynamic-stdc++"}, ) - use_repo(llvm, "llvm_toolchain") diff --git a/third_party/traceability/tools/source_code_linker/get_git_info.py b/third_party/traceability/tools/source_code_linker/get_git_info.py index fd01498..7794e0b 100644 --- a/third_party/traceability/tools/source_code_linker/get_git_info.py +++ b/third_party/traceability/tools/source_code_linker/get_git_info.py @@ -19,11 +19,13 @@ import subprocess from pathlib import Path + def get_github_repo() -> str: git_root = find_git_root() repo = get_github_repo_info(git_root) return repo + def parse_git_output(str_line: str) -> str: if len(str_line.split()) < 2: logger.warning( diff --git a/third_party/traceability/tools/source_code_linker/parse_source_files.py b/third_party/traceability/tools/source_code_linker/parse_source_files.py index 482c899..6a42ff0 100644 --- a/third_party/traceability/tools/source_code_linker/parse_source_files.py +++ b/third_party/traceability/tools/source_code_linker/parse_source_files.py @@ -39,20 +39,23 @@ ] lobster_code_template = { - "data": [], - "generator": "lobster_cpp", - "schema": "lobster-imp-trace", - "version": 3 - } + "data": [], + "generator": "lobster_cpp", + "schema": "lobster-imp-trace", + "version": 3, +} lobster_reqs_template = { - "data": [], - "generator": "lobster-trlc", - "schema": "lobster-req-trace", - "version": 4 - } + "data": [], + "generator": "lobster-trlc", + "schema": "lobster-req-trace", + "version": 4, +} -def extract_id_from_line(line: str, tags: List[str], nodes: List[str]) -> Optional[Tuple[str, str]]: + +def extract_id_from_line( + line: str, tags: List[str], nodes: List[str] +) -> Optional[Tuple[str, str]]: """ Parse a single line to extract the ID from tags or nodes. @@ -90,10 +93,12 @@ def extract_id_from_line(line: str, tags: List[str], nodes: List[str]) -> Option None """ # Step 1: Clean the line of $, \n, and \\n - cleaned_line = line.replace('\n', '').replace('\\n', '') + cleaned_line = line.replace("\n", "").replace("\\n", "") # Step 2: Remove all single and double quotes - cleaned_line = cleaned_line.replace('"', '').replace("'", '').replace("{",'').strip() + cleaned_line = ( + cleaned_line.replace('"', "").replace("'", "").replace("{", "").strip() + ) # Step 3 and 4: Search for tags or nodes and capture the last element for tag in tags: @@ -103,16 +108,16 @@ def extract_id_from_line(line: str, tags: List[str], nodes: List[str]) -> Option for node in nodes: if cleaned_line.startswith(node): # Scan for Macro - if node.startswith('$'): - parts = cleaned_line.split(',') + if node.startswith("$"): + parts = cleaned_line.split(",") if len(parts) >= 2: - return parts[1].strip().rstrip(')'), node - else: # scan for normal plantuml element + return parts[1].strip().rstrip(")"), node + else: # scan for normal plantuml element # Remove the identifier from the start of the line - parts = cleaned_line[len(node):].strip().split() + parts = cleaned_line[len(node) :].strip().split() # If there are at least 3 parts and the second-to-last is 'as', return the last part - if len(parts) >= 3 and parts[-2] == 'as': + if len(parts) >= 3 and parts[-2] == "as": return parts[-1], node # If there's only one part after the identifier, return it @@ -121,11 +126,12 @@ def extract_id_from_line(line: str, tags: List[str], nodes: List[str]) -> Option return None + def extract_tags( source_file: str, github_base_url: str, nodes: List[str], - git_hash_func: Union[Callable[[str], str], None] = get_git_hash + git_hash_func: Union[Callable[[str], str], None] = get_git_hash, ) -> Dict[str, List[Tuple[str, str]]]: """ This extracts the file-path, lineNr as well as the git hash of the file @@ -142,7 +148,9 @@ def extract_tags( if git_hash_func is None: git_hash_func = get_git_hash - requirement_mapping: dict[str, List[Tuple[str, str]]] = collections.defaultdict(list) + requirement_mapping: dict[str, List[Tuple[str, str]]] = collections.defaultdict( + list + ) with open(source_file) as f: hash = git_hash_func(source_file) for line_number, line in enumerate(f): @@ -159,11 +167,12 @@ def extract_tags( return requirement_mapping + def _extract_tags_dispatch( source_file: str, github_base_url: str, mode=str, - git_hash_func: Union[Callable[[str], str], None] = get_git_hash + git_hash_func: Union[Callable[[str], str], None] = get_git_hash, ) -> Dict[str, List[str]]: """ Dispatch to a specialized parser based on file extension. @@ -176,7 +185,7 @@ def _extract_tags_dispatch( if mode == "reqs": foo = lobster_reqs_template - rm = extract_tags(source_file, github_base_url, [], git_hash_func) + rm = extract_tags(source_file, github_base_url, [], git_hash_func) for id, item in rm.items(): link, node = item[0] requirement = { @@ -193,9 +202,9 @@ def _extract_tags_dispatch( "just_down": [], "just_global": [], "framework": "TRLC", - "kind": node.replace('$','').strip(), + "kind": node.replace("$", "").strip(), "text": f"{id}", - } + } foo["data"].append(requirement) else: @@ -217,16 +226,15 @@ def _extract_tags_dispatch( "just_up": [], "just_down": [], "just_global": [], - "refs": [ - f"req {id}" - ], + "refs": [f"req {id}"], "language": "cpp", - "kind": "Function" - } + "kind": "Function", + } foo["data"].append(codetag) return foo + if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("-o", "--output") @@ -253,14 +261,18 @@ def _extract_tags_dispatch( logger.info(f"Parsing source files: {args.inputs}") # Finding the GH URL - gh_base_url=f"{args.url}{get_github_repo()}" + gh_base_url = f"{args.url}{get_github_repo()}" - requirement_mappings: Dict[str, List[Tuple[str, str]]] = collections.defaultdict(list) + requirement_mappings: Dict[str, List[Tuple[str, str]]] = collections.defaultdict( + list + ) for input in args.inputs: with open(input) as f: for source_file in f: - foo = _extract_tags_dispatch(source_file.strip(), gh_base_url, args.trace) + foo = _extract_tags_dispatch( + source_file.strip(), gh_base_url, args.trace + ) if not foo: if args.trace == "reqs": diff --git a/third_party/traceability/tools/trlc_renderer/trlc_renderer.py b/third_party/traceability/tools/trlc_renderer/trlc_renderer.py index 8d2cb6f..1cdddc2 100644 --- a/third_party/traceability/tools/trlc_renderer/trlc_renderer.py +++ b/third_party/traceability/tools/trlc_renderer/trlc_renderer.py @@ -15,15 +15,24 @@ 3: "^^^^^^^^^^", } + class TRLCRenderer: - def __init__(self, input_directory: str, output_path: str, mapping_file: str = None, debug: bool = False): + def __init__( + self, + input_directory: str, + output_path: str, + mapping_file: str = None, + debug: bool = False, + ): self.input_directory = input_directory self.output_path = output_path self.debug = debug self.symbols = None self.requirements = None self.req_objects = None - self.type_mapping = self.load_type_mapping(mapping_file) # Load the mapping file if provided + self.type_mapping = self.load_type_mapping( + mapping_file + ) # Load the mapping file if provided if self.debug: logging.basicConfig(level=logging.DEBUG) @@ -31,7 +40,7 @@ def __init__(self, input_directory: str, output_path: str, mapping_file: str = N def load_type_mapping(self, mapping_file: str) -> dict: """Load the requirement type mapping from a JSON file or return an empty mapping.""" if mapping_file: - with open(mapping_file, 'r') as file: + with open(mapping_file, "r") as file: mapping = json.load(file) logging.debug("Loaded type mapping successfully.") return mapping @@ -66,7 +75,14 @@ def convert_symbols_to_objects(self): if from_node.is_root: continue - found_node = next((to_node for to_node in to_tree.children if to_node.name == from_node.name), None) + found_node = next( + ( + to_node + for to_node in to_tree.children + if to_node.name == from_node.name + ), + None, + ) if found_node is None: to_tree.append(from_node) break @@ -80,7 +96,7 @@ def convert_symbols_to_objects(self): def apply_case_format(self, value: str, case_format: str) -> str: """Apply the specified case format to a string.""" if case_format == "snake_case": - return re.sub(r'(? str: def generate_need_id(self, reqobj) -> str: """Generates a unique identifier for a requirement object based on its type, package, - and its name using the format specified in the mapping file.""" + and its name using the format specified in the mapping file.""" req_type = reqobj.n_typ.name req_package = reqobj.n_package.name req_name = reqobj.name @@ -105,44 +121,67 @@ def generate_need_id(self, reqobj) -> str: # Get the casing formats id_case_format = self.type_mapping.get(req_type, {}).get("id_case_format", {}) - formatted_type = self.apply_case_format(mapped_type, id_case_format.get("type", "")) - formatted_package = self.apply_case_format(req_package, id_case_format.get("package", "")) - formatted_name = self.apply_case_format(req_name, id_case_format.get("name", "")) + formatted_type = self.apply_case_format( + mapped_type, id_case_format.get("type", "") + ) + formatted_package = self.apply_case_format( + req_package, id_case_format.get("package", "") + ) + formatted_name = self.apply_case_format( + req_name, id_case_format.get("name", "") + ) # Format the ID using the specified format - return id_format.format(type=formatted_type, package=formatted_package, name=formatted_name) + return id_format.format( + type=formatted_type, package=formatted_package, name=formatted_name + ) def generate_link_id_score(self, link, objects) -> str: """Generates a unique identifier for a link object based on its identifier and version, - using the same format as defined in generate_id_format.""" - linkobj = objects[link['item']] + using the same format as defined in generate_id_format.""" + linkobj = objects[link["item"]] # Use the same ID format for links return self.generate_need_id(linkobj) + f"@{link['LinkVersion']}" def get_link_attribute_value(self, value) -> str: """Generates the attribute value for link attributes.""" - return ', '.join(self.generate_link_id_score(v, self.req_objects) for v in value if v is not None) + return ", ".join( + self.generate_link_id_score(v, self.req_objects) + for v in value + if v is not None + ) def map_requirement_type(self, req_type: str) -> tuple: """Maps the requirement type using the loaded mapping or returns the original type and attributes.""" mapping = self.type_mapping.get(req_type) if mapping is None: - logging.debug(f"Type {req_type} not found in mapping, using {req_type} with default attributes.") - return req_type, [], [] # Return original type and empty attributes if not found + logging.debug( + f"Type {req_type} not found in mapping, using {req_type} with default attributes." + ) + return ( + req_type, + [], + [], + ) # Return original type and empty attributes if not found # Extract the mapped type, attributes, and links mapped_type = mapping.get("mapped_type", req_type) attributes = mapping.get("attributes", []) links = mapping.get("links", []) - return mapped_type, attributes, links # Return mapped type, attributes, and links + return ( + mapped_type, + attributes, + links, + ) # Return mapped type, attributes, and links + def _convert_to_title(self, identifier: str) -> str: - transformed_title="" + transformed_title = "" for i in identifier: if i.isupper(): - transformed_title+=" "+i + transformed_title += " " + i else: - transformed_title+=i + transformed_title += i return transformed_title.join(" ") def render_restructured_text_file(self): @@ -156,7 +195,9 @@ def render_restructured_text_file(self): if node.is_leaf: reqobj = self.req_objects[node.name] req_type = reqobj.n_typ.name # Get the requirement type - mapped_type, attributes_to_export, links_to_export = self.map_requirement_type(req_type) # Use mapping + mapped_type, attributes_to_export, links_to_export = ( + self.map_requirement_type(req_type) + ) # Use mapping title = self._convert_to_title(node.name) id = self.generate_need_id(reqobj) @@ -168,8 +209,8 @@ def render_restructured_text_file(self): reqobjpython_dict = reqobj.to_python_dict() for attr in attributes_to_export: # Split the attribute and default value if specified - if '=' in attr: - key, default_value = map(str.strip, attr.split('=', 1)) + if "=" in attr: + key, default_value = map(str.strip, attr.split("=", 1)) else: key, default_value = attr, "Not specified" @@ -185,12 +226,19 @@ def render_restructured_text_file(self): else: # Handle regular attributes if isinstance(value, list): - attr_val = ', '.join( - str(v) if not isinstance(v, dict) else ', '.join(f"{v2}" for v2 in v.values()) - for v in value if v is not None + attr_val = ", ".join( + str(v) + if not isinstance(v, dict) + else ", ".join(f"{v2}" for v2 in v.values()) + for v in value + if v is not None ) else: - attr_val = str(value) if not isinstance(value, dict) else ', '.join(f"{v2}" for v2 in value.values()) + attr_val = ( + str(value) + if not isinstance(value, dict) + else ", ".join(f"{v2}" for v2 in value.values()) + ) file.write(f" :{key}: {attr_val}\n") @@ -219,9 +267,13 @@ def run(self): def argument_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser() parser.add_argument("-o", "--output", required=True, help="Output file path") - parser.add_argument("-m", "--mapping", help="Path to the JSON mapping file (optional)") - parser.add_argument("--debug", action='store_true', help="Enable debug output") - parser.add_argument("--debugpy", action='store_true', help="Enable debugpy for debugging") + parser.add_argument( + "-m", "--mapping", help="Path to the JSON mapping file (optional)" + ) + parser.add_argument("--debug", action="store_true", help="Enable debug output") + parser.add_argument( + "--debugpy", action="store_true", help="Enable debugpy for debugging" + ) return parser From 1d53174f18dde1c49f7fc4f5d464bd9332fbd140 Mon Sep 17 00:00:00 2001 From: devendrapat <76650287+devendrapat@users.noreply.github.com> Date: Fri, 19 Dec 2025 16:04:28 +0530 Subject: [PATCH 08/34] Setup initial workflow with gcc build (#7) * Setup initial workflow with gcc build * Remove release_verification.yml from workflow --- .../workflows/build_and_test_host_gcc12.yml | 39 +++++++++++++++++++ .github/workflows/copyright.yml | 24 ------------ .github/workflows/docs-cleanup.yml | 29 -------------- .github/workflows/format.yml | 26 ------------- .github/workflows/gitlint.yml | 31 --------------- .../workflows/{docs.yml => test_and_docs.yml} | 17 ++++++-- 6 files changed, 52 insertions(+), 114 deletions(-) create mode 100644 .github/workflows/build_and_test_host_gcc12.yml delete mode 100644 .github/workflows/copyright.yml delete mode 100644 .github/workflows/docs-cleanup.yml delete mode 100644 .github/workflows/format.yml delete mode 100644 .github/workflows/gitlint.yml rename .github/workflows/{docs.yml => test_and_docs.yml} (76%) diff --git a/.github/workflows/build_and_test_host_gcc12.yml b/.github/workflows/build_and_test_host_gcc12.yml new file mode 100644 index 0000000..e379946 --- /dev/null +++ b/.github/workflows/build_and_test_host_gcc12.yml @@ -0,0 +1,39 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* + +# Workflow configuration for S-CORE CI - Bazel Build & Test logging +# This workflow runs Bazel build and test when triggered by specific pull request events. + +name: Bazel Build & Test logging (with host toolchain GCC12.2) +on: + pull_request: + types: [opened, reopened, synchronize] + push: + branches: + - main + merge_group: + types: [checks_requested] +jobs: + build_and_test_gcc_host_gcc12: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4.2.2 + - name: Setup Bazel + uses: bazel-contrib/setup-bazel@0.9.1 + - name: Bazel build logging targets + run: | + bazel build //score/... + - name: Bazel test logging targets + run: | + bazel test //score/... diff --git a/.github/workflows/copyright.yml b/.github/workflows/copyright.yml deleted file mode 100644 index 08ef376..0000000 --- a/.github/workflows/copyright.yml +++ /dev/null @@ -1,24 +0,0 @@ -# ******************************************************************************* -# Copyright (c) 2024 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 -# ******************************************************************************* - -name: Copyright checks -on: - pull_request: - types: [opened, reopened, synchronize] - merge_group: - types: [checks_requested] -jobs: - copyright-check: - uses: eclipse-score/cicd-workflows/.github/workflows/copyright.yml@main - with: - bazel-target: "run //:copyright.check" diff --git a/.github/workflows/docs-cleanup.yml b/.github/workflows/docs-cleanup.yml deleted file mode 100644 index cfa4ae2..0000000 --- a/.github/workflows/docs-cleanup.yml +++ /dev/null @@ -1,29 +0,0 @@ -# ******************************************************************************* -# 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 -# ******************************************************************************* - -name: Documentation Cleanup - -permissions: - contents: write - pages: write - id-token: write - -on: - schedule: - - cron: '0 0 * * *' # Runs every day at midnight UTC - -jobs: - docs-cleanup: - uses: eclipse-score/cicd-workflows/.github/workflows/docs-cleanup.yml@main - secrets: - token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml deleted file mode 100644 index 620d697..0000000 --- a/.github/workflows/format.yml +++ /dev/null @@ -1,26 +0,0 @@ -# ******************************************************************************* -# Copyright (c) 2024 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 -# ******************************************************************************* - -name: Formatting checks - -on: - pull_request: - types: [opened, reopened, synchronize] - merge_group: - types: [checks_requested] - -jobs: - formatting-check: - uses: eclipse-score/cicd-workflows/.github/workflows/format.yml@main - with: - bazel-target: "test //:format.check" # optional, this is the default diff --git a/.github/workflows/gitlint.yml b/.github/workflows/gitlint.yml deleted file mode 100644 index 90487ed..0000000 --- a/.github/workflows/gitlint.yml +++ /dev/null @@ -1,31 +0,0 @@ -# ******************************************************************************* -# Copyright (c) 2024 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 -# ******************************************************************************* - -name: Gitlint check -on: - pull_request: - types: [opened, synchronize, reopened] -jobs: - lint-commits: - name: check-commit-messages - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - name: Run Gitlint Action - if: ${{ github.event_name == 'pull_request' }} - uses: ./.github/actions/gitlint - with: - pr-number: ${{ github.event.number }} diff --git a/.github/workflows/docs.yml b/.github/workflows/test_and_docs.yml similarity index 76% rename from .github/workflows/docs.yml rename to .github/workflows/test_and_docs.yml index 24bd399..8771cb0 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/test_and_docs.yml @@ -11,7 +11,7 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* -name: Documentation +name: Tests, Verify and Build Docs permissions: contents: write @@ -29,15 +29,24 @@ on: types: [checks_requested] jobs: - build-docs: + docs-verify: + uses: eclipse-score/cicd-workflows/.github/workflows/docs-verify.yml@main + permissions: + pull-requests: write + contents: read + with: + bazel-docs-verify-target: "//:docs_check" + + # TODO skipping tests as we don't integrate test results in docs anyways + + docs-build: + needs: [docs-verify] uses: eclipse-score/cicd-workflows/.github/workflows/docs.yml@main permissions: contents: write pages: write pull-requests: write id-token: write - with: - # the bazel-target depends on your repo specific docs_targets configuration (e.g. "suffix") bazel-target: "//:docs -- --github_user=${{ github.repository_owner }} --github_repo=${{ github.event.repository.name }}" retention-days: 3 From d4db3b236a7651e52f7ac877182b36cbb68b621d Mon Sep 17 00:00:00 2001 From: Pawel Rutka Date: Fri, 19 Dec 2025 14:38:17 +0100 Subject: [PATCH 09/34] Extend codeowners (#9) --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d96c068..8445204 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,2 +1,2 @@ # default owners -* @antonkri @rmaddikery @hoppe-and-dreams @mobileinfo @andreapefe @RSingh1511 @Y-Vaishnavi +* @antonkri @rmaddikery @hoppe-and-dreams @mobileinfo @andreapefe @RSingh1511 @Y-Vaishnavi @pawelrutkaq @arkjedrz From 14634198aeb28c93026eca39fde3b92efb35f77a Mon Sep 17 00:00:00 2001 From: Arvid Sievert Date: Fri, 19 Dec 2025 14:52:00 +0100 Subject: [PATCH 10/34] Extend codeowners (#10) Signed-off-by: Arvid Sievert --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 8445204..f0adbb6 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,2 +1,2 @@ # default owners -* @antonkri @rmaddikery @hoppe-and-dreams @mobileinfo @andreapefe @RSingh1511 @Y-Vaishnavi @pawelrutkaq @arkjedrz +* @antonkri @rmaddikery @hoppe-and-dreams @mobileinfo @andreapefe @RSingh1511 @Y-Vaishnavi @pawelrutkaq @arkjedrz @arsibo From dd7d866a4cdbe70a1d1fc572f0cbfced1f6c6360 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20J=C4=99drzejewski?= <32277297+arkjedrz@users.noreply.github.com> Date: Tue, 30 Dec 2025 15:25:15 +0100 Subject: [PATCH 11/34] infra: fix copyright headers (#16) - Fix copyright config. - Fix missing copyrights. - Fix formatting. - Make `//:copyright.check` work. --- .bazelrc | 5 ++++ .../workflows/build_and_test_host_gcc12.yml | 26 +++++++++---------- BUILD | 3 +++ examples/BUILD | 13 ++++++++++ .../include/daemon/i_diagnostic_job_parser.h | 1 - score/datarouter/include/dlt/dlt_common.h | 12 +++++++++ score/datarouter/include/dlt/dlt_protocol.h | 12 +++++++++ score/datarouter/include/dlt/dlt_types.h | 12 +++++++++ 8 files changed, 70 insertions(+), 14 deletions(-) diff --git a/.bazelrc b/.bazelrc index ff537e8..e7df623 100644 --- a/.bazelrc +++ b/.bazelrc @@ -11,6 +11,11 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +build --java_language_version=17 +build --tool_java_language_version=17 +build --java_runtime_version=remotejdk_17 +build --tool_java_runtime_version=remotejdk_17 + common --@score_baselibs//score/memory/shared/flags:use_typedshmd=False common --@score_baselibs//score/json:base_library=nlohmann diff --git a/.github/workflows/build_and_test_host_gcc12.yml b/.github/workflows/build_and_test_host_gcc12.yml index e379946..ff9f2e1 100644 --- a/.github/workflows/build_and_test_host_gcc12.yml +++ b/.github/workflows/build_and_test_host_gcc12.yml @@ -24,16 +24,16 @@ on: merge_group: types: [checks_requested] jobs: - build_and_test_gcc_host_gcc12: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4.2.2 - - name: Setup Bazel - uses: bazel-contrib/setup-bazel@0.9.1 - - name: Bazel build logging targets - run: | - bazel build //score/... - - name: Bazel test logging targets - run: | - bazel test //score/... + build_and_test_gcc_host_gcc12: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4.2.2 + - name: Setup Bazel + uses: bazel-contrib/setup-bazel@0.9.1 + - name: Bazel build logging targets + run: | + bazel build //score/... + - name: Bazel test logging targets + run: | + bazel test //score/... diff --git a/BUILD b/BUILD index 7726cd2..ffda439 100644 --- a/BUILD +++ b/BUILD @@ -24,8 +24,11 @@ copyright_checker( srcs = [ ".github", "docs", + "examples", + "score", "src", "tests", + "//:.bazelrc", "//:BUILD", "//:MODULE.bazel", "//:project_config.bzl", diff --git a/examples/BUILD b/examples/BUILD index 012dd54..d1031a1 100644 --- a/examples/BUILD +++ b/examples/BUILD @@ -1,3 +1,16 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* + # Needed for Dash tool to check python dependency licenses. filegroup( name = "cargo_lock", diff --git a/score/datarouter/include/daemon/i_diagnostic_job_parser.h b/score/datarouter/include/daemon/i_diagnostic_job_parser.h index 58f5c81..29ee5c2 100644 --- a/score/datarouter/include/daemon/i_diagnostic_job_parser.h +++ b/score/datarouter/include/daemon/i_diagnostic_job_parser.h @@ -1,4 +1,3 @@ - /******************************************************************************** * Copyright (c) 2025 Contributors to the Eclipse Foundation * diff --git a/score/datarouter/include/dlt/dlt_common.h b/score/datarouter/include/dlt/dlt_common.h index 93a67ee..ffd75bc 100644 --- a/score/datarouter/include/dlt/dlt_common.h +++ b/score/datarouter/include/dlt/dlt_common.h @@ -1,3 +1,15 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ /* * @licence app begin@ * SPDX license identifier: MPL-2.0 diff --git a/score/datarouter/include/dlt/dlt_protocol.h b/score/datarouter/include/dlt/dlt_protocol.h index 0d2409b..44e79c5 100644 --- a/score/datarouter/include/dlt/dlt_protocol.h +++ b/score/datarouter/include/dlt/dlt_protocol.h @@ -1,3 +1,15 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ /* * @licence app begin@ * SPDX license identifier: MPL-2.0 diff --git a/score/datarouter/include/dlt/dlt_types.h b/score/datarouter/include/dlt/dlt_types.h index 7902579..63e8d25 100644 --- a/score/datarouter/include/dlt/dlt_types.h +++ b/score/datarouter/include/dlt/dlt_types.h @@ -1,3 +1,15 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ /* * @licence app begin@ * SPDX license identifier: MPL-2.0 From 83b8cffa9089540449f9efd53279c493d45e8a48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20J=C4=99drzejewski?= <32277297+arkjedrz@users.noreply.github.com> Date: Tue, 30 Dec 2025 15:52:57 +0100 Subject: [PATCH 12/34] infra: fix `bazel build //...` (#17) - Remove unnecessary `CONTRIBUTING.md` and `WORKSPACE`. - Bump dependencies versions where necessary. - Replace dep target for Rust logging backend. - Workaround docs build issue. --- CONTRIBUTING.md | 68 -------------------------------- MODULE.bazel | 9 +---- WORKSPACE | 12 ------ docs/conf.py | 2 +- src/rust/mw_log_subscriber/BUILD | 3 +- 5 files changed, 5 insertions(+), 89 deletions(-) delete mode 100644 CONTRIBUTING.md delete mode 100644 WORKSPACE diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 1ad3ec1..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,68 +0,0 @@ -# Contributing to the Logging Module - -- Thank you for your interest in contributing to the Logging Module of the Eclipse SCORE project! -- This guide will walk you through the necessary steps to get started with development, adhere to our standards, and successfully submit your contributions. - -## Eclipse Contributor Agreement (ECA) - -Before making any contributions, **you must sign the Eclipse Contributor Agreement (ECA)**. This is a mandatory requirement for all contributors to Eclipse projects. - -Sign the ECA here: https://www.eclipse.org/legal/ECA.php - -Pull requests from contributors who have not signed the ECA **will not be accepted**. - -## Contribution Workflow - -To ensure a smooth contribution process, please follow the steps below: - -1. **Fork** the repository to your GitHub account. -2. **Create a feature branch** from your fork: - ```bash - git checkout -b feature/your-feature-name - ``` -3. **Write clean and modular code**, adhering to: - - **C++17 standard** - - **Google C++ Style Guide** -4. **Add tests** for any new functionality. -5. **Ensure all tests pass** before submitting: - ```bash - bazel test //... - ``` -6. **Open a Pull Request** from your feature branch to the `main` branch of the upstream repository. - - Provide a clear title and description of your changes. - - Reference any related issues. - -## Development Setup - -To build and test the Logging Module, follow the steps below from the project root: - -```bash -# Build all targets using default toolchain -bazel build //... - -# Run all unit tests -bazel test //... -``` - -**Prerequisites:** Install [Bazelisk](https://github.com/bazelbuild/bazelisk) to manage Bazel versions. See [Bazel tutorial](https://bazel.build/tutorials) for details. - -## Testing - -The Logging Module includes extensive tests. Use the following commands: - -### Run All Tests -```bash -bazel test //... -``` - -### Test Categories -- **Unit Tests**: Unit-level testing -- **Component Tests**: Component-level testing -- **Integration Tests**: Cross-component interactions -- **Safety Tests**: ASIL-B compliance verification - -## Additional Resources - -For project details, documentation, and support resources, please refer to the main [README.md](README.md). - -**Thank you for contributing to the Eclipse SCORE Logging Module!** diff --git a/MODULE.bazel b/MODULE.bazel index 990f071..fce6cd2 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -93,12 +93,7 @@ bazel_dep(name = "rules_cc", version = "0.2.1") bazel_dep(name = "nlohmann_json", version = "3.11.3") bazel_dep(name = "bazel_skylib", version = "1.7.1") bazel_dep(name = "rules_doxygen", version = "2.5.0") -bazel_dep(name = "score_baselibs", version = "0.2.0") -git_override( - module_name = "score_baselibs", - commit = "baab4419469b8b74af87d7cc77d3f2605f8055a9", - remote = "https://github.com/eclipse-score/baselibs.git", -) +bazel_dep(name = "score_baselibs", version = "0.2.1") # Python 3.12 toolchain for Bazel (required for LOBSTER/TRLC dependencies) bazel_dep(name = "rules_python", version = "1.4.1") @@ -155,4 +150,4 @@ bazel_dep(name = "aspect_rules_lint", version = "1.5.3") bazel_dep(name = "buildifier_prebuilt", version = "8.2.0.2") #docs-as-code -bazel_dep(name = "score_docs_as_code", version = "2.0.2") +bazel_dep(name = "score_docs_as_code", version = "2.2.0") diff --git a/WORKSPACE b/WORKSPACE deleted file mode 100644 index 8acad8f..0000000 --- a/WORKSPACE +++ /dev/null @@ -1,12 +0,0 @@ -# ******************************************************************************* -# Copyright (c) 2024 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 -# ******************************************************************************* diff --git a/docs/conf.py b/docs/conf.py index cf13475..64d78f2 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -34,7 +34,7 @@ "sphinx_design", "sphinx_needs", "sphinxcontrib.plantuml", - "score_plantuml", + # "score_plantuml", "score_metamodel", "score_draw_uml_funcs", "score_source_code_linker", diff --git a/src/rust/mw_log_subscriber/BUILD b/src/rust/mw_log_subscriber/BUILD index aefb83c..285d303 100644 --- a/src/rust/mw_log_subscriber/BUILD +++ b/src/rust/mw_log_subscriber/BUILD @@ -19,7 +19,8 @@ cc_library( srcs = ["src/rust_cpp_log_adapter.cpp"], visibility = ["//visibility:private"], deps = [ - "@score_baselibs//score/mw/log", + "//score/mw/log/detail/common:recorder_factory", + "@score_baselibs//score/mw/log:frontend", ], ) From db52508050a80671d32f6923505bba27e8adad27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20J=C4=99drzejewski?= <32277297+arkjedrz@users.noreply.github.com> Date: Tue, 6 Jan 2026 08:24:51 +0100 Subject: [PATCH 13/34] infra: add CI workflows (#15) * Initial commit * Log backend for rust frontend (#1) * Log backend for rust frontend This is logger impl that bridges rust log frontend with C++ mw_log implementation Signed-off-by: Pawel Rutka * Adress review findings * Adapt to cpp/rust structure as in persistency * Align to baselibs --------- Signed-off-by: Pawel Rutka * repo: align dependecies to newest S-CORE state (#5) * infra: add CI workflows - Add CI workflows. - Align Bazel config and deps. - Fix build-preventing and other minor issues. * infra: post-review fixes Fixes after review. --------- Signed-off-by: Pawel Rutka Co-authored-by: eclipse-otterdog[bot] <158182605+eclipse-otterdog[bot]@users.noreply.github.com> Co-authored-by: Pawel Rutka --- .bazelrc | 15 +- .github/ISSUE_TEMPLATE/bug_report.md | 24 +++ .github/actions/gitlint/action.yml | 2 +- .github/pull_request_template.md | 33 ++++ .github/workflows/build.yml | 65 +++++++ .github/workflows/build_qnx8.yml | 35 ++++ .github/workflows/copyright.yml | 27 +++ .github/workflows/docs-cleanup.yml | 29 ++++ .../workflows/{test_and_docs.yml => docs.yml} | 17 +- .github/workflows/format.yml | 27 +++ .github/workflows/gitlint.yml | 34 ++++ .github/workflows/license_check.yml | 33 ++++ ...uild_and_test_host_gcc12.yml => tests.yml} | 31 ++-- .gitignore | 1 + MODULE.bazel | 163 +++++++++--------- rustfmt.toml | 4 + scripts/internal/qnx_creds.py | 81 +++++++++ src/rust/mw_log_subscriber/examples/main.rs | 14 +- src/rust/mw_log_subscriber/src/lib.rs | 12 +- 19 files changed, 520 insertions(+), 127 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/pull_request_template.md create mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/build_qnx8.yml create mode 100644 .github/workflows/copyright.yml create mode 100644 .github/workflows/docs-cleanup.yml rename .github/workflows/{test_and_docs.yml => docs.yml} (76%) create mode 100644 .github/workflows/format.yml create mode 100644 .github/workflows/gitlint.yml create mode 100644 .github/workflows/license_check.yml rename .github/workflows/{build_and_test_host_gcc12.yml => tests.yml} (60%) create mode 100644 rustfmt.toml create mode 100644 scripts/internal/qnx_creds.py diff --git a/.bazelrc b/.bazelrc index e7df623..7796054 100644 --- a/.bazelrc +++ b/.bazelrc @@ -17,9 +17,9 @@ build --java_runtime_version=remotejdk_17 build --tool_java_runtime_version=remotejdk_17 common --@score_baselibs//score/memory/shared/flags:use_typedshmd=False +common --@score_baselibs//score/mw/log/flags:KRemote_Logging=False common --@score_baselibs//score/json:base_library=nlohmann - 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 @@ -27,14 +27,23 @@ common --//score/datarouter/build_configuration_flags:enable_dynamic_configurati common --//score/datarouter/build_configuration_flags:dlt_file_transfer_feature=False common --//score/datarouter/build_configuration_flags:use_local_vlan=True -common --extra_toolchains=@gcc_toolchain//:host_gcc_12 build --incompatible_strict_action_env test --test_tag_filters=-manual test --test_output=errors build --experimental_retain_test_configuration_across_testonly #https://github.com/bazelbuild/bazel/issues/6842 -common --registry=https://raw.githubusercontent.com/eclipse-score/bazel_registry/refs/heads/main/ +common --registry=https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/ common --registry=https://bcr.bazel.build +common --credential_helper=*.qnx.com=%workspace%/scripts/internal/qnx_creds.py + +build:build_qnx8 --platforms=@score_bazel_platforms//:arm64-qnx8_0 +build:build_qnx8 --extra_toolchains=@toolchains_qnx_qcc//:qcc_aarch64 +build:build_qnx8 --extra_toolchains=@score_toolchains_rust//toolchains/aarch64-unknown-qnx8_0:toolchain_aarch64_qnx8_0 +build:build_qnx8 --extra_toolchains=@toolchains_qnx_ifs//:ifs_x86_64 +build:build_qnx8 --extra_toolchains=@toolchains_qnx_ifs//:ifs_aarch64 + +common --extra_toolchains=@gcc_toolchain//:host_gcc_12 +common --extra_toolchains=@score_toolchains_rust//toolchains/x86_64-unknown-linux-gnu:toolchain_x86_64_linux # With this instrumentation filter for our two main components, we ensure that `bazel coverage //...` is yielding the correct results coverage --instrumentation_filter="^//score/datarouter[/:],^//score/mw/log[/:],-//score/mw/.*/test[/:]" diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..bc9c72f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,24 @@ +--- +name: Bug report +about: Create a report for a bug +title: 'Bug: Your bug title' +labels: 'bug' +assignees: '' + +--- + +### Describe the bug: + + +### Steps to reproduce the behavior: +1. +2. + +### Observed behavior: + +### Expected behavior + +### Occurrence: + + +### Attachments / Logs: diff --git a/.github/actions/gitlint/action.yml b/.github/actions/gitlint/action.yml index cb16e6f..da48456 100644 --- a/.github/actions/gitlint/action.yml +++ b/.github/actions/gitlint/action.yml @@ -1,5 +1,5 @@ # ******************************************************************************* -# Copyright (c) 2024 Contributors to the Eclipse Foundation +# Copyright (c) 2025 Contributors to the Eclipse Foundation # # See the NOTICE file(s) distributed with this work for additional # information regarding copyright ownership. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..893cba0 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,33 @@ + + + +## Notes for Reviewer + + +## Pre-Review Checklist for the PR Author + +* [ ] PR title is short, expressive and meaningful +* [ ] Commits are properly organized +* [ ] Relevant issues are linked in the [References](#references) section +* [ ] Tests are conducted +* [ ] Unit tests are added + +## Checklist for the PR Reviewer + +* [ ] Commits are properly organized and messages are according to the guideline +* [ ] Unit tests have been written for new behavior +* [ ] Public API is documented +* [ ] PR title describes the changes + +## Post-review Checklist for the PR Author + +* [ ] All open points are addressed and tracked via issues + +## References + + + +Closes # + + + diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..7070a22 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,65 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* + +name: Bazel Build + +on: + pull_request: + types: [opened, reopened, synchronize] + merge_group: + types: [checks_requested] + +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SCORE_QNX_USER: ${{ secrets.SCORE_QNX_USER }} + SCORE_QNX_PASSWORD: ${{ secrets.SCORE_QNX_PASSWORD }} + SCORE_QNX_LICENSE: ${{ secrets.SCORE_QNX_LICENSE }} +jobs: + build: + name: Build Bazel Code + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4.2.2 + + - name: Setup Bazel + uses: bazel-contrib/setup-bazel@0.15.0 + with: + bazelisk-version: 1.26.0 # newest LTS before 1 Jun 2025 + disk-cache: true + repository-cache: true + bazelisk-cache: true + + - name: Bazel info (discover paths) + id: bazel-info + run: | + echo "BAZEL_OUTPUT_BASE=$(bazel info output_base)" >> $GITHUB_ENV + echo "BAZEL_USER_ROOT=$(bazel info output_user_root)" >> $GITHUB_ENV + echo "BAZEL_REPO_CACHE=$(bazel info repository_cache)" >> $GITHUB_ENV + bazel info + + - name: Cache Bazel output base + uses: actions/cache@v4 + with: + path: | + ${{ env.BAZEL_OUTPUT_BASE }}/action_cache + ${{ env.BAZEL_OUTPUT_BASE }}/bazel-out + ${{ env.BAZEL_OUTPUT_BASE }}/external + ${{ env.BAZEL_OUTPUT_BASE }}/execroot + key: bazel-ob-v2-${{ runner.os }}-${{ hashFiles('.bazelversion', 'MODULE.bazel', 'MODULE.bazel.lock', '**/*.bzl') }} + restore-keys: | + bazel-ob-v2-${{ runner.os }}- + + - name: Build with Bazel + run: | + bazel build //... diff --git a/.github/workflows/build_qnx8.yml b/.github/workflows/build_qnx8.yml new file mode 100644 index 0000000..2fed313 --- /dev/null +++ b/.github/workflows/build_qnx8.yml @@ -0,0 +1,35 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* + +name: QNX8 Build +on: + pull_request_target: + types: [opened, reopened, synchronize] + merge_group: + types: [checks_requested] +jobs: + qnx-build: + if: false # temporarily disable QNX builds + uses: eclipse-score/cicd-workflows/.github/workflows/qnx-build.yml@main + permissions: + contents: read + pull-requests: read + with: + bazel-target: "//score/... //src/..." + bazel-config: "build_qnx8" + credential-helper: "scripts/internal/qnx_creds.py" + environment-name: "workflow-approval" + secrets: + score-qnx-license: ${{ secrets.SCORE_QNX_LICENSE }} + score-qnx-user: ${{ secrets.SCORE_QNX_USER }} + score-qnx-password: ${{ secrets.SCORE_QNX_PASSWORD }} diff --git a/.github/workflows/copyright.yml b/.github/workflows/copyright.yml new file mode 100644 index 0000000..8483278 --- /dev/null +++ b/.github/workflows/copyright.yml @@ -0,0 +1,27 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* + +name: Copyright checks +on: + pull_request: + types: [opened, reopened, synchronize] + merge_group: + types: [checks_requested] +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + +jobs: + copyright-check: + uses: eclipse-score/cicd-workflows/.github/workflows/copyright.yml@main + with: + bazel-target: "run //:copyright.check" diff --git a/.github/workflows/docs-cleanup.yml b/.github/workflows/docs-cleanup.yml new file mode 100644 index 0000000..56626f0 --- /dev/null +++ b/.github/workflows/docs-cleanup.yml @@ -0,0 +1,29 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* + +name: Documentation Cleanup + +permissions: + contents: write + pages: write + id-token: write + +on: + schedule: + - cron: "0 0 * * *" # Runs every day at midnight UTC + +jobs: + docs-cleanup: + uses: eclipse-score/cicd-workflows/.github/workflows/docs-cleanup.yml@main + secrets: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/test_and_docs.yml b/.github/workflows/docs.yml similarity index 76% rename from .github/workflows/test_and_docs.yml rename to .github/workflows/docs.yml index 8771cb0..24bd399 100644 --- a/.github/workflows/test_and_docs.yml +++ b/.github/workflows/docs.yml @@ -11,7 +11,7 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* -name: Tests, Verify and Build Docs +name: Documentation permissions: contents: write @@ -29,24 +29,15 @@ on: types: [checks_requested] jobs: - docs-verify: - uses: eclipse-score/cicd-workflows/.github/workflows/docs-verify.yml@main - permissions: - pull-requests: write - contents: read - with: - bazel-docs-verify-target: "//:docs_check" - - # TODO skipping tests as we don't integrate test results in docs anyways - - docs-build: - needs: [docs-verify] + build-docs: uses: eclipse-score/cicd-workflows/.github/workflows/docs.yml@main permissions: contents: write pages: write pull-requests: write id-token: write + with: + # the bazel-target depends on your repo specific docs_targets configuration (e.g. "suffix") bazel-target: "//:docs -- --github_user=${{ github.repository_owner }} --github_repo=${{ github.event.repository.name }}" retention-days: 3 diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml new file mode 100644 index 0000000..adb8876 --- /dev/null +++ b/.github/workflows/format.yml @@ -0,0 +1,27 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* + +name: Formatting checks + +on: + pull_request: + types: [opened, reopened, synchronize] + merge_group: + types: [checks_requested] +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +jobs: + formatting-check: + uses: eclipse-score/cicd-workflows/.github/workflows/format.yml@main + with: + bazel-target: "test //:format.check" # optional, this is the default diff --git a/.github/workflows/gitlint.yml b/.github/workflows/gitlint.yml new file mode 100644 index 0000000..e4e667b --- /dev/null +++ b/.github/workflows/gitlint.yml @@ -0,0 +1,34 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* + +name: Gitlint check +on: + pull_request: + types: [opened, synchronize, reopened] +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +jobs: + lint-commits: + name: check-commit-messages + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Run Gitlint Action + if: ${{ github.event_name == 'pull_request' }} + uses: ./.github/actions/gitlint + with: + pr-number: ${{ github.event.number }} + base-branch: ${{ github.event.pull_request.base.ref }} diff --git a/.github/workflows/license_check.yml b/.github/workflows/license_check.yml new file mode 100644 index 0000000..8e7cfe1 --- /dev/null +++ b/.github/workflows/license_check.yml @@ -0,0 +1,33 @@ +# ******************************************************************************* +# 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 +# ******************************************************************************* + +name: License check preparation +on: + pull_request_target: + types: [opened, reopened, synchronize] + merge_group: + types: [checks_requested] +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +permissions: + pull-requests: write + issues: write + +jobs: + license-check: + if: ${{ github.repository == 'eclipse-score/logging' }} + uses: eclipse-score/cicd-workflows/.github/workflows/license-check.yml@main + with: + repo-url: "${{ github.server_url }}/${{ github.repository }}" + secrets: + dash-api-token: ${{ secrets.ECLIPSE_GITLAB_API_TOKEN }} diff --git a/.github/workflows/build_and_test_host_gcc12.yml b/.github/workflows/tests.yml similarity index 60% rename from .github/workflows/build_and_test_host_gcc12.yml rename to .github/workflows/tests.yml index ff9f2e1..bd09372 100644 --- a/.github/workflows/build_and_test_host_gcc12.yml +++ b/.github/workflows/tests.yml @@ -11,29 +11,32 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* -# Workflow configuration for S-CORE CI - Bazel Build & Test logging -# This workflow runs Bazel build and test when triggered by specific pull request events. +name: Bazel Tests -name: Bazel Build & Test logging (with host toolchain GCC12.2) on: pull_request: types: [opened, reopened, synchronize] - push: - branches: - - main merge_group: types: [checks_requested] +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + jobs: - build_and_test_gcc_host_gcc12: + setup-and-run-tests: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4.2.2 - - name: Setup Bazel - uses: bazel-contrib/setup-bazel@0.9.1 - - name: Bazel build logging targets - run: | - bazel build //score/... - - name: Bazel test logging targets + + - name: Setup Bazel with shared caching + uses: bazel-contrib/setup-bazel@0.15.0 + with: + bazelisk-version: 1.26.0 + disk-cache: true + repository-cache: true + bazelisk-cache: true + + - name: Run Tests via Bazel run: | - bazel test //score/... + echo "Running: bazel test //score/... //src/..." + bazel test //score/... //src/... diff --git a/.gitignore b/.gitignore index eaa02c4..3caabce 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,7 @@ user.bazelrc .cache/* compile_commands.json rust-project.json + # Ruff .ruff_cache diff --git a/MODULE.bazel b/MODULE.bazel index fce6cd2..62821f8 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -16,16 +16,55 @@ module( compatibility_level = 0, ) +# Bazel global rules +bazel_dep(name = "rules_python", version = "1.4.1") +bazel_dep(name = "bazel_skylib", version = "1.7.1") +bazel_dep(name = "rules_rust", version = "0.61.0") +bazel_dep(name = "rules_cc", version = "0.1.1") +bazel_dep(name = "aspect_rules_lint", version = "1.0.3") +bazel_dep(name = "buildifier_prebuilt", version = "7.3.1") bazel_dep(name = "platforms", version = "1.0.0") -bazel_dep(name = "score_toolchains_gcc", version = "0.4", dev_dependency = True) +# S-CORE process rules +bazel_dep(name = "score_bazel_platforms", version = "0.0.3") +bazel_dep(name = "score_docs_as_code", version = "2.2.0") +bazel_dep(name = "score_tooling", version = "1.0.4") +bazel_dep(name = "score_rust_policies", version = "0.0.3") -bazel_dep(name = "googletest", version = "1.17.0.bcr.1") -bazel_dep(name = "google_benchmark", version = "1.9.4") -bazel_dep(name = "rules_rust", version = "0.63.0") -bazel_dep(name = "rapidjson", version = "1.1.0") -bazel_dep(name = "score_crates", version = "0.0.3", repo_name = "crate_index") -bazel_dep(name = "jemalloc", version = "5.3.0-bcr.alpha.4") +bazel_dep(name = "score_process", version = "1.4.0", dev_dependency = True) +bazel_dep(name = "score_platform", version = "0.5.1", dev_dependency = True) # This is main score repo + +# Toolchains and extensions +bazel_dep(name = "score_toolchains_gcc", version = "0.5", dev_dependency = True) +bazel_dep(name = "score_toolchains_qnx", version = "0.0.6", dev_dependency = True) +bazel_dep(name = "rust_qnx8_toolchain", version = "1.2.0", dev_dependency = True) +bazel_dep(name = "score_toolchains_rust", version = "0.1.1", dev_dependency = True) + +# S-CORE crates +bazel_dep(name = "score_crates", version = "0.0.6") + +# Overrides +git_override( + module_name = "score_tooling", + commit = "612d6f180a9bb6338de5f0e6667fcf83068d9c37", #until 1.0.5 is released + remote = "https://github.com/eclipse-score/tooling.git", +) + +git_override( + module_name = "score_toolchains_rust", + commit = "bcf8e5364f72cf136ec81960350a82e2b5c45449", + remote = "https://github.com/eclipse-score/toolchains_rust.git", +) + +archive_override( + module_name = "rust_qnx8_toolchain", + strip_prefix = "qnx8", + urls = [ + "https://github.com/qorix-group/rust-lang-qnx8/releases/download/1.2.0/qnx8_rust_toolchain.tar.gz", + ], +) + +# Extensions gcc = use_extension("@score_toolchains_gcc//extentions:gcc.bzl", "gcc", dev_dependency = True) gcc.toolchain( @@ -42,34 +81,8 @@ git_override( remote = "https://github.com/bmw-software-engineering/trlc.git", ) -# TODO to be moved to toolchain. https://github.com/eclipse-score/toolchains_gcc/issues/11 -gcc.extra_features( - features = [ - "minimal_warnings", - "treat_warnings_as_errors", - ], -) -gcc.warning_flags( - minimal_warnings = [ - "-Wall", - "-Wno-error=deprecated-declarations", - ], - strict_warnings = [ - "-Wextra", - "-Wpedantic", - ], - treat_warnings_as_errors = ["-Werror"], -) use_repo(gcc, "gcc_toolchain", "gcc_toolchain_gcc") -bazel_dep(name = "rules_boost", repo_name = "com_github_nelhage_rules_boost") -archive_override( - module_name = "rules_boost", - strip_prefix = "rules_boost-master", - urls = ["https://github.com/nelhage/rules_boost/archive/refs/heads/master.tar.gz"], -) - -bazel_dep(name = "boost.program_options", version = "1.87.0") bazel_dep(name = "download_utils", version = "1.0.1") download_archive = use_repo_rule("@download_utils//download/archive:defs.bzl", "download_archive") @@ -88,42 +101,46 @@ download_archive( urls = ["https://github.com/python-jsonschema/jsonschema/archive/refs/tags/v4.23.0.tar.gz"], ) -# C/C++ rules for Bazel -bazel_dep(name = "rules_cc", version = "0.2.1") -bazel_dep(name = "nlohmann_json", version = "3.11.3") -bazel_dep(name = "bazel_skylib", version = "1.7.1") -bazel_dep(name = "rules_doxygen", version = "2.5.0") -bazel_dep(name = "score_baselibs", version = "0.2.1") +toolchains_qnx = use_extension("@score_toolchains_qnx//:extensions.bzl", "toolchains_qnx", dev_dependency = True) +toolchains_qnx.sdp( + sha256 = "f2e0cb21c6baddbcb65f6a70610ce498e7685de8ea2e0f1648f01b327f6bac63", + strip_prefix = "installation", + url = "https://www.qnx.com/download/download/79858/installation.tgz", +) -# Python 3.12 toolchain for Bazel (required for LOBSTER/TRLC dependencies) -bazel_dep(name = "rules_python", version = "1.4.1") +PYTHON_VERSION = "3.12" -python = use_extension("@rules_python//python/extensions:python.bzl", "python") +python = use_extension("@rules_python//python/extensions:python.bzl", "python", dev_dependency = True) python.toolchain( - python_version = "3.12", + is_default = True, + python_version = PYTHON_VERSION, ) +use_repo(python) -bazel_dep(name = "score_toolchains_qnx", version = "0.0.2", dev_dependency = True) -git_override( - module_name = "score_toolchains_qnx", - commit = "f9fe820390eede3d4e9b2a35651ff78bdd3cb676", - remote = "https://github.com/eclipse-score/toolchains_qnx.git", -) +use_repo(toolchains_qnx, "toolchains_qnx_sdp") +use_repo(toolchains_qnx, "toolchains_qnx_qcc") +use_repo(toolchains_qnx, "toolchains_qnx_ifs") -toolchains_qnx = use_extension( - "@score_toolchains_qnx//:extensions.bzl", - "toolchains_qnx", - dev_dependency = True, +# C++ dependencies + +bazel_dep(name = "googletest", version = "1.17.0.bcr.1") +bazel_dep(name = "google_benchmark", version = "1.9.4") +bazel_dep(name = "rapidjson", version = "1.1.0") +bazel_dep(name = "jemalloc", version = "5.3.0-bcr.alpha.4") +bazel_dep(name = "libatomic", version = "1.0") +local_path_override( + module_name = "libatomic", + path = "third_party/libatomic", ) -toolchains_qnx.sdp( - sha256 = "f2e0cb21c6baddbcb65f6a70610ce498e7685de8ea2e0f1648f01b327f6bac63", - strip_prefix = "installation", - url = "https://www.qnx.com/download/download/79858/installation.tgz", + +bazel_dep(name = "rules_boost", repo_name = "com_github_nelhage_rules_boost") +archive_override( + module_name = "rules_boost", + strip_prefix = "rules_boost-master", + urls = ["https://github.com/nelhage/rules_boost/archive/refs/heads/master.tar.gz"], ) -use_repo(toolchains_qnx, "toolchains_qnx_sdp") -use_repo(toolchains_qnx, "toolchains_qnx_ifs") -use_repo(toolchains_qnx, "toolchains_qnx_qcc") +bazel_dep(name = "boost.program_options", version = "1.87.0") bazel_dep(name = "score_communication", version = "0.1.2") git_override( module_name = "score_communication", @@ -131,23 +148,11 @@ git_override( remote = "https://github.com/eclipse-score/communication.git", ) -# LLVM Toolchains Rules - host configuration -bazel_dep(name = "toolchains_llvm", version = "1.4.0") - -llvm = use_extension("@toolchains_llvm//toolchain/extensions:llvm.bzl", "llvm") -llvm.toolchain( - cxx_standard = {"": "c++17"}, - llvm_version = "19.1.0", +bazel_dep(name = "nlohmann_json", version = "3.11.3") +bazel_dep(name = "rules_doxygen", version = "2.5.0") +bazel_dep(name = "score_baselibs", version = "0.2.0") +git_override( + module_name = "score_baselibs", + commit = "3c65b223e9f516f95935bb4cd2e83d6088ca016f", + remote = "https://github.com/eclipse-score/baselibs.git", ) -use_repo(llvm, "llvm_toolchain") -use_repo(llvm, "llvm_toolchain_llvm") - -register_toolchains("@llvm_toolchain//:all") - -# tooling -bazel_dep(name = "score_tooling", version = "1.0.1") -bazel_dep(name = "aspect_rules_lint", version = "1.5.3") -bazel_dep(name = "buildifier_prebuilt", version = "8.2.0.2") - -#docs-as-code -bazel_dep(name = "score_docs_as_code", version = "2.2.0") diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..85b6056 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,4 @@ +# `rustfmt` should not be used with local configuration. +# Use Bazel target for formatting. +# E.g., `bazel run //:format.fix_Rust_with_rustfmt` +DO_NOT_USE_LOCAL_RUSTFMT_TOML = false diff --git a/scripts/internal/qnx_creds.py b/scripts/internal/qnx_creds.py new file mode 100644 index 0000000..06ae795 --- /dev/null +++ b/scripts/internal/qnx_creds.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 + +# ******************************************************************************* +# 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 +# ******************************************************************************* + +import http.cookiejar +import json +import netrc +import os +import sys +import urllib.parse +import urllib.request + + +def eprint(*args, **kwargs): + print(*args, file=sys.stderr, **kwargs) + + +if __name__ == "__main__": + data = json.load(sys.stdin) + + if "qnx.com" not in data["uri"]: + eprint("Unsupported domain") + sys.exit(1) + + if "SCORE_QNX_USER" in os.environ and "SCORE_QNX_PASSWORD" in os.environ: + login = os.environ["SCORE_QNX_USER"] + password = os.environ["SCORE_QNX_PASSWORD"] + else: + try: + nrc = netrc.netrc() + auth = nrc.authenticators("qnx.com") + if auth: + login, _, password = auth + else: + raise Exception("No credential found for QNX") + except Exception as excp: + eprint(excp) + eprint("Failed getting credentials from .netrc") + sys.exit(1) + + data = urllib.parse.urlencode( + {"userlogin": login, "password": password, "UseCookie": "1"} + ) + data = data.encode("ascii") + + cookie_jar = http.cookiejar.CookieJar() + cookie_processor = urllib.request.HTTPCookieProcessor(cookie_jar) + opener = urllib.request.build_opener(cookie_processor) + urllib.request.install_opener(opener) + + r = urllib.request.urlopen("https://www.qnx.com/account/login.html", data) + if r.status != 200: + eprint("Failed to login to QNX") + sys.exit(1) + + cookies = {c.name: c.value for c in list(cookie_jar)} + if not "myQNX" in cookies: + eprint("Failed to get myQNX cookie from login page") + sys.exit(1) + + myQNX = cookies["myQNX"] + print( + json.dumps( + { + "headers": { + "Cookie": [f"myQNX={myQNX}"], + } + } + ) + ) diff --git a/src/rust/mw_log_subscriber/examples/main.rs b/src/rust/mw_log_subscriber/examples/main.rs index 169686d..d31299e 100644 --- a/src/rust/mw_log_subscriber/examples/main.rs +++ b/src/rust/mw_log_subscriber/examples/main.rs @@ -49,13 +49,13 @@ fn main() { error!( "This is an log that will be trimmed {} {} {} {} {} {} {}. END MARKER NOT VISIBLE", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", - "ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", - "ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", - "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", + "ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", + "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg" ); // Using logger instance with context diff --git a/src/rust/mw_log_subscriber/src/lib.rs b/src/rust/mw_log_subscriber/src/lib.rs index 410994f..41aecfe 100644 --- a/src/rust/mw_log_subscriber/src/lib.rs +++ b/src/rust/mw_log_subscriber/src/lib.rs @@ -34,9 +34,7 @@ impl MwLoggerBuilder { } /// Builds the MwLogger with the specified context and configuration and returns it. - pub fn build( - self, - ) -> MwLogger { + pub fn build(self) -> MwLogger { let context_cstr = self.context.unwrap_or(CString::new("DFLT").unwrap()); let c_logger_ptr = unsafe { mw_log_create_logger(context_cstr.as_ptr().cast::()) }; MwLogger { @@ -46,13 +44,7 @@ impl MwLoggerBuilder { } /// Builds and sets the MwLogger as the default logger with the specified configuration. - pub fn set_as_default_logger< - const SHOW_MODULE: bool, - const SHOW_FILE: bool, - const SHOW_LINE: bool, - >( - self, - ) { + pub fn set_as_default_logger(self) { let logger = self.build::(); log::set_max_level(mw_log_logger_level(logger.ptr)); log::set_boxed_logger(Box::new(logger)) From 4f9703b3ebdb9a76cc62ea4d5c7fc9a6c35bcf7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20J=C4=99drzejewski?= <32277297+arkjedrz@users.noreply.github.com> Date: Tue, 6 Jan 2026 08:51:06 +0100 Subject: [PATCH 14/34] infra: enable QNX8 build (#23) Was temporarily disabled. --- .github/workflows/build_qnx8.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build_qnx8.yml b/.github/workflows/build_qnx8.yml index 2fed313..83174d0 100644 --- a/.github/workflows/build_qnx8.yml +++ b/.github/workflows/build_qnx8.yml @@ -19,7 +19,6 @@ on: types: [checks_requested] jobs: qnx-build: - if: false # temporarily disable QNX builds uses: eclipse-score/cicd-workflows/.github/workflows/qnx-build.yml@main permissions: contents: read From fbb528525bcf89f1728619fa3b3077b24addb668 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20J=C4=99drzejewski?= <32277297+arkjedrz@users.noreply.github.com> Date: Tue, 6 Jan 2026 09:00:15 +0100 Subject: [PATCH 15/34] repo: add `rust-analyzer` support (#22) Add script to initialize `rust-analyzer` support. --- .vscode/settings.json | 13 +++++-------- README.md | 6 ++++++ scripts/generate_rust_analyzer_support.sh | 6 ++++++ 3 files changed, 17 insertions(+), 8 deletions(-) create mode 100755 scripts/generate_rust_analyzer_support.sh diff --git a/.vscode/settings.json b/.vscode/settings.json index 47cb954..ed35220 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,12 +5,10 @@ "files.trimTrailingWhitespace": true, "editor.insertSpaces": true, "editor.tabCompletion": "on", - // Default for any filetype "editor.rulers": [ 99 ], - // Exclude build, temp and cache folders "files.watcherExclude": { ".*/**": true, @@ -19,7 +17,6 @@ ".venv*/**": true, "_build/**": true, }, - // Python Settings // Exclude build, temp and cache folders "python.analysis.exclude": [ @@ -42,22 +39,20 @@ }, "editor.defaultFormatter": "charliermarsh.ruff", }, - // Markdown Settings "[markdown]": { // We mostly write markdown in some combination with python, // so we use the same rulers as python. "editor.rulers": [ - 79, 99 + 79, + 99 ] }, - "bazel.lsp.command": "bazel", "bazel.lsp.args": [ "run", "//:starpls_server" ], - // RST Settings "[restructuredtext]": { "editor.tabSize": 3, @@ -99,8 +94,10 @@ "--ignore-glob=bazel-*/*", "--ignore-glob=.venv_docs/*", "--ignore-glob=_build/*", - ], "python.testing.unittestEnabled": false, "python.testing.pytestEnabled": true, + "rust-analyzer.linkedProjects": [ + "${workspaceFolder}/rust-project.json" + ], } diff --git a/README.md b/README.md index 1e8af75..0dd2bd3 100644 --- a/README.md +++ b/README.md @@ -112,3 +112,9 @@ PROJECT_CONFIG = { When used with macros like `dash_license_checker`, it allows dynamic selection of file types (e.g., `cargo`, `requirements`) based on the languages declared in `source_code`. + +## IDE support + +### Rust + +Use `scripts/generate_rust_analyzer_support.sh` to generate `rust_analyzer` settings that will let VS Code work. diff --git a/scripts/generate_rust_analyzer_support.sh b/scripts/generate_rust_analyzer_support.sh new file mode 100755 index 0000000..2f78bc7 --- /dev/null +++ b/scripts/generate_rust_analyzer_support.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -e + +# Manual targets are not take into account, must be set explicitly +bazel run @rules_rust//tools/rust_analyzer:gen_rust_project -- "@//examples/..." "@//src/..." "@//tests/..." From 561b140cfb50eb14add2b5b315e5eddd53b89c59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20J=C4=99drzejewski?= <32277297+arkjedrz@users.noreply.github.com> Date: Mon, 12 Jan 2026 08:31:05 +0100 Subject: [PATCH 16/34] bazel: remove unused dependencies (#21) - Remove unused dependencies (non-exhaustive). - Remove unused `third_party` files. --- BUILD | 3 +- MODULE.bazel | 36 ------------------- .../detail/data_router/shared_memory/BUILD | 1 - .../log/detail/wait_free_producer_queue/BUILD | 1 - third_party/dependencies.bzl | 18 ---------- third_party/host_gcc/BUILD | 0 third_party/host_gcc/host_gcc.MODULE.bazel | 28 --------------- third_party/host_llvm/BUILD | 0 third_party/host_llvm/host_llvm.MODULE.bazel | 25 ------------- third_party/json_schema_validator/BUILD | 12 ------- .../json_schema_validator.BUILD | 33 ----------------- third_party/jsonschema/BUILD | 17 --------- third_party/jsonschema/jsonschema.BUILD | 20 ----------- third_party/libatomic/BUILD | 9 ----- third_party/libatomic/MODULE.bazel | 4 --- third_party/libatomic/libatomic.BUILD | 23 ------------ third_party/libatomic/libatomic.bzl | 14 -------- 17 files changed, 1 insertion(+), 243 deletions(-) delete mode 100644 third_party/dependencies.bzl delete mode 100644 third_party/host_gcc/BUILD delete mode 100644 third_party/host_gcc/host_gcc.MODULE.bazel delete mode 100644 third_party/host_llvm/BUILD delete mode 100644 third_party/host_llvm/host_llvm.MODULE.bazel delete mode 100644 third_party/json_schema_validator/BUILD delete mode 100644 third_party/json_schema_validator/json_schema_validator.BUILD delete mode 100644 third_party/jsonschema/BUILD delete mode 100644 third_party/jsonschema/jsonschema.BUILD delete mode 100644 third_party/libatomic/BUILD delete mode 100644 third_party/libatomic/MODULE.bazel delete mode 100644 third_party/libatomic/libatomic.BUILD delete mode 100644 third_party/libatomic/libatomic.bzl diff --git a/BUILD b/BUILD index ffda439..b1c9667 100644 --- a/BUILD +++ b/BUILD @@ -11,8 +11,7 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* load("@score_docs_as_code//:docs.bzl", "docs") -load("@score_tooling//:defs.bzl", "copyright_checker", "dash_license_checker", "setup_starpls", "use_format_targets") -load("//:project_config.bzl", "PROJECT_CONFIG") +load("@score_tooling//:defs.bzl", "copyright_checker", "setup_starpls", "use_format_targets") setup_starpls( name = "starpls_server", diff --git a/MODULE.bazel b/MODULE.bazel index 62821f8..f048bc3 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -83,24 +83,6 @@ git_override( use_repo(gcc, "gcc_toolchain", "gcc_toolchain_gcc") -bazel_dep(name = "download_utils", version = "1.0.1") - -download_archive = use_repo_rule("@download_utils//download/archive:defs.bzl", "download_archive") - -download_archive( - name = "json_schema_validator", - build = "//third_party/json_schema_validator:json_schema_validator.BUILD", - strip_prefix = "json-schema-validator-2.1.0", - urls = ["https://github.com/pboettch/json-schema-validator/archive/refs/tags/2.1.0.tar.gz"], -) - -download_archive( - name = "jsonschema", - build = "//third_party/jsonschema:jsonschema.BUILD", - strip_prefix = "jsonschema-4.23.0", - urls = ["https://github.com/python-jsonschema/jsonschema/archive/refs/tags/v4.23.0.tar.gz"], -) - toolchains_qnx = use_extension("@score_toolchains_qnx//:extensions.bzl", "toolchains_qnx", dev_dependency = True) toolchains_qnx.sdp( sha256 = "f2e0cb21c6baddbcb65f6a70610ce498e7685de8ea2e0f1648f01b327f6bac63", @@ -124,23 +106,7 @@ use_repo(toolchains_qnx, "toolchains_qnx_ifs") # C++ dependencies bazel_dep(name = "googletest", version = "1.17.0.bcr.1") -bazel_dep(name = "google_benchmark", version = "1.9.4") bazel_dep(name = "rapidjson", version = "1.1.0") -bazel_dep(name = "jemalloc", version = "5.3.0-bcr.alpha.4") -bazel_dep(name = "libatomic", version = "1.0") -local_path_override( - module_name = "libatomic", - path = "third_party/libatomic", -) - -bazel_dep(name = "rules_boost", repo_name = "com_github_nelhage_rules_boost") -archive_override( - module_name = "rules_boost", - strip_prefix = "rules_boost-master", - urls = ["https://github.com/nelhage/rules_boost/archive/refs/heads/master.tar.gz"], -) - -bazel_dep(name = "boost.program_options", version = "1.87.0") bazel_dep(name = "score_communication", version = "0.1.2") git_override( module_name = "score_communication", @@ -148,8 +114,6 @@ git_override( remote = "https://github.com/eclipse-score/communication.git", ) -bazel_dep(name = "nlohmann_json", version = "3.11.3") -bazel_dep(name = "rules_doxygen", version = "2.5.0") bazel_dep(name = "score_baselibs", version = "0.2.0") git_override( module_name = "score_baselibs", diff --git a/score/mw/log/detail/data_router/shared_memory/BUILD b/score/mw/log/detail/data_router/shared_memory/BUILD index c337381..6179879 100644 --- a/score/mw/log/detail/data_router/shared_memory/BUILD +++ b/score/mw/log/detail/data_router/shared_memory/BUILD @@ -140,7 +140,6 @@ cc_test( ":reader", ":writer", "//score/mw/log/test/console_logging_environment", - "//third_party/libatomic", "@googletest//:gtest_main", "@score_baselibs//score/os/mocklib:fcntl_mock", "@score_baselibs//score/os/mocklib:mman_mock", diff --git a/score/mw/log/detail/wait_free_producer_queue/BUILD b/score/mw/log/detail/wait_free_producer_queue/BUILD index 54f7831..730a712 100644 --- a/score/mw/log/detail/wait_free_producer_queue/BUILD +++ b/score/mw/log/detail/wait_free_producer_queue/BUILD @@ -108,7 +108,6 @@ cc_test( "alternating_writer", "read_only_reader", "//score/mw/log/test/console_logging_environment", - "//third_party/libatomic", "@googletest//:gtest_main", ], ) diff --git a/third_party/dependencies.bzl b/third_party/dependencies.bzl deleted file mode 100644 index 27137bd..0000000 --- a/third_party/dependencies.bzl +++ /dev/null @@ -1,18 +0,0 @@ -"""Third-party dependencies configuration for eclipse-score-logging. - -This module manages external dependencies for the eclipse-score-logging project. -Currently, only essential dependencies required for the minimal logging toolchain -are loaded and initialized. - -Active dependencies: - - libatomic: Atomic operations library support - -Usage: - Load this module in your WORKSPACE file and call third_party_deps() to - initialize all third-party dependencies. -""" - -load("@//third_party/libatomic:libatomic.bzl", "libatomic") - -def third_party_deps(): - libatomic() diff --git a/third_party/host_gcc/BUILD b/third_party/host_gcc/BUILD deleted file mode 100644 index e69de29..0000000 diff --git a/third_party/host_gcc/host_gcc.MODULE.bazel b/third_party/host_gcc/host_gcc.MODULE.bazel deleted file mode 100644 index 807312d..0000000 --- a/third_party/host_gcc/host_gcc.MODULE.bazel +++ /dev/null @@ -1,28 +0,0 @@ -bazel_dep(name = "score_toolchains_gcc", version = "0.4", dev_dependency = True) - -gcc = use_extension("@score_toolchains_gcc//extentions:gcc.bzl", "gcc", dev_dependency = True) -gcc.toolchain( - sha256 = "457f5f20f57528033cb840d708b507050d711ae93e009388847e113b11bf3600", - strip_prefix = "x86_64-unknown-linux-gnu", - url = "https://github.com/eclipse-score/toolchains_gcc_packages/releases/download/0.0.1/x86_64-unknown-linux-gnu_gcc12.tar.gz", -) - -# TODO to be moved to toolchain. https://github.com/eclipse-score/toolchains_gcc/issues/11 -gcc.extra_features( - features = [ - "minimal_warnings", - "treat_warnings_as_errors", - ], -) -gcc.warning_flags( - minimal_warnings = [ - "-Wall", - "-Wno-error=deprecated-declarations", - ], - strict_warnings = [ - "-Wextra", - "-Wpedantic", - ], - treat_warnings_as_errors = ["-Werror"], -) -use_repo(gcc, "gcc_toolchain", "gcc_toolchain_gcc") diff --git a/third_party/host_llvm/BUILD b/third_party/host_llvm/BUILD deleted file mode 100644 index e69de29..0000000 diff --git a/third_party/host_llvm/host_llvm.MODULE.bazel b/third_party/host_llvm/host_llvm.MODULE.bazel deleted file mode 100644 index 2f6a772..0000000 --- a/third_party/host_llvm/host_llvm.MODULE.bazel +++ /dev/null @@ -1,25 +0,0 @@ -bazel_dep(name = "toolchains_llvm", version = "1.5.0", dev_dependency = True) - -llvm = use_extension("@toolchains_llvm//toolchain/extensions:llvm.bzl", "llvm", dev_dependency = True) -llvm.toolchain( - compile_flags = {"": [ - "-march=nehalem", - "-ffp-model=strict", - # Security - "-U_FORTIFY_SOURCE", # https://github.com/google/sanitizers/issues/247 - "-fstack-protector", - "-fno-omit-frame-pointer", - # Diagnostics - "-fcolor-diagnostics", - "-Wno-deprecated-declarations", - "-Wno-error=self-assign-overloaded", - "-Wthread-safety", - ]}, - cxx_standard = {"": "c++17"}, - link_libs = {"": [ - "-lrt", - ]}, - llvm_version = "16.0.0", - stdlib = {"": "dynamic-stdc++"}, -) -use_repo(llvm, "llvm_toolchain") diff --git a/third_party/json_schema_validator/BUILD b/third_party/json_schema_validator/BUILD deleted file mode 100644 index 6bdeed2..0000000 --- a/third_party/json_schema_validator/BUILD +++ /dev/null @@ -1,12 +0,0 @@ -# ******************************************************************************* -# 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 -# ******************************************************************************* diff --git a/third_party/json_schema_validator/json_schema_validator.BUILD b/third_party/json_schema_validator/json_schema_validator.BUILD deleted file mode 100644 index b9f6964..0000000 --- a/third_party/json_schema_validator/json_schema_validator.BUILD +++ /dev/null @@ -1,33 +0,0 @@ -# ******************************************************************************* -# 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 -# ******************************************************************************* -package( - default_visibility = ["//visibility:public"], -) - -cc_library( - name = "json_schema_validator_lib", - srcs = glob(["src/*"]), - hdrs = ["src/nlohmann/json-schema.hpp"], - features = [ - "third_party_warnings", - "-treat_warnings_as_errors", - ], - includes = ["src"], - deps = ["@nlohmann_json//:json"], -) - -cc_binary( - name = "json_schema_validator", - srcs = ["app/json-schema-validate.cpp"], - deps = [":json_schema_validator_lib"], -) diff --git a/third_party/jsonschema/BUILD b/third_party/jsonschema/BUILD deleted file mode 100644 index 23dbd4a..0000000 --- a/third_party/jsonschema/BUILD +++ /dev/null @@ -1,17 +0,0 @@ -# ******************************************************************************* -# 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 -# ******************************************************************************* -alias( - name = "jsonschema", - actual = "@jsonschema//:lib", - visibility = ["//visibility:public"], -) diff --git a/third_party/jsonschema/jsonschema.BUILD b/third_party/jsonschema/jsonschema.BUILD deleted file mode 100644 index 3bacc56..0000000 --- a/third_party/jsonschema/jsonschema.BUILD +++ /dev/null @@ -1,20 +0,0 @@ -# ******************************************************************************* -# 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 -# ******************************************************************************* -py_library( - name = "lib", - srcs = glob([ - "jsonschema/**/*.py", - ]), - imports = ["."], - visibility = ["//visibility:public"], -) diff --git a/third_party/libatomic/BUILD b/third_party/libatomic/BUILD deleted file mode 100644 index 10a9804..0000000 --- a/third_party/libatomic/BUILD +++ /dev/null @@ -1,9 +0,0 @@ -cc_library( - name = "libatomic", - linkopts = select({ - "@platforms//os:linux": ["-latomic"], # Link actual system libatomic - "@platforms//os:qnx": ["-latomic"], # QNX also needs libatomic - "//conditions:default": [], # Other platforms don't need it - }), - visibility = ["//visibility:public"], -) diff --git a/third_party/libatomic/MODULE.bazel b/third_party/libatomic/MODULE.bazel deleted file mode 100644 index 10c3aaf..0000000 --- a/third_party/libatomic/MODULE.bazel +++ /dev/null @@ -1,4 +0,0 @@ -module( - name = "libatomic", - version = "1.0", -) diff --git a/third_party/libatomic/libatomic.BUILD b/third_party/libatomic/libatomic.BUILD deleted file mode 100644 index cdb20e2..0000000 --- a/third_party/libatomic/libatomic.BUILD +++ /dev/null @@ -1,23 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -libatomic_libs = [ - "usr/lib/x86_64-linux-gnu/libatomic.so.1", - "usr/lib/x86_64-linux-gnu/libatomic.so.1.2.0", -] - -cc_library( - name = "libatomic", - srcs = libatomic_libs, - visibility = [ - "//visibility:public", - ], -) - -# A dedicated filegroup that exposes only the shared libraries (without headers). -# This is used to provide .so files to packaging tools (e.g., for .deb creation), -# which do not require headers or full C++ dependency metadata. -# It simplifies packaging logic and avoids unintentionally pulling in development files. -filegroup( - name = "libatomic_solibs", - srcs = libatomic_libs, -) diff --git a/third_party/libatomic/libatomic.bzl b/third_party/libatomic/libatomic.bzl deleted file mode 100644 index 4cb1c3c..0000000 --- a/third_party/libatomic/libatomic.bzl +++ /dev/null @@ -1,14 +0,0 @@ -load("@//third_party:codecraft_remotes.bzl", "CC_UBUNTU_ARCHIVE") -load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") -load("@swh_bazel_rules//artifactory:artifactory_deb.bzl", "artifactory_deb_group") - -def libatomic(): - maybe( - artifactory_deb_group, - name = "libatomic", - build_file = "//third_party/libatomic:libatomic.BUILD", - repo = CC_UBUNTU_ARCHIVE, - package_group = { - "pool/main/g/gcc-10//libatomic1_10.5.0-1ubuntu1~20.04_amd64.deb": "29b19adba1cb79b0cbf905ee21cc68d3a88eadcd56c39d1a0b29c5a523b60557", - }, - ) From b427b4eeb33276970d15a84fb991d3a50b2276fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20J=C4=99drzejewski?= <32277297+arkjedrz@users.noreply.github.com> Date: Thu, 15 Jan 2026 10:08:51 +0100 Subject: [PATCH 17/34] repo: remove `src/` from CI (#26) `src/` will be removed in next commit. --- .github/workflows/build_qnx8.yml | 2 +- .github/workflows/tests.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build_qnx8.yml b/.github/workflows/build_qnx8.yml index 83174d0..7ec5c4c 100644 --- a/.github/workflows/build_qnx8.yml +++ b/.github/workflows/build_qnx8.yml @@ -24,7 +24,7 @@ jobs: contents: read pull-requests: read with: - bazel-target: "//score/... //src/..." + bazel-target: "//score/..." bazel-config: "build_qnx8" credential-helper: "scripts/internal/qnx_creds.py" environment-name: "workflow-approval" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index bd09372..4128c57 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -38,5 +38,5 @@ jobs: - name: Run Tests via Bazel run: | - echo "Running: bazel test //score/... //src/..." - bazel test //score/... //src/... + echo "Running: bazel test //score/..." + bazel test //score/... From c13952ab83f668b163115cad8d5e9b27e4541688 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20J=C4=99drzejewski?= <32277297+arkjedrz@users.noreply.github.com> Date: Thu, 15 Jan 2026 10:20:25 +0100 Subject: [PATCH 18/34] repo: move `src/` to `score` (#25) Move components under `src/` to `score/` directory. --- BUILD | 1 - README.md | 8 ++++---- docs/index.rst | 4 ++-- .../mw/log/rust/mw_logger}/BUILD | 6 +++--- .../mw/log/rust/mw_logger}/examples/config/logging.json | 0 .../mw/log/rust/mw_logger}/examples/main.rs | 2 +- .../mw/log/rust/mw_logger}/src/lib.rs | 0 .../mw/log/rust/mw_logger}/src/mw_log_ffi.rs | 0 .../mw/log/rust/mw_logger}/src/rust_cpp_log_adapter.cpp | 0 scripts/generate_rust_analyzer_support.sh | 2 +- src/BUILD | 0 src/cpp/.gitkeep | 0 12 files changed, 11 insertions(+), 12 deletions(-) rename {src/rust/mw_log_subscriber => score/mw/log/rust/mw_logger}/BUILD (93%) rename {src/rust/mw_log_subscriber => score/mw/log/rust/mw_logger}/examples/config/logging.json (100%) rename {src/rust/mw_log_subscriber => score/mw/log/rust/mw_logger}/examples/main.rs (98%) rename {src/rust/mw_log_subscriber => score/mw/log/rust/mw_logger}/src/lib.rs (100%) rename {src/rust/mw_log_subscriber => score/mw/log/rust/mw_logger}/src/mw_log_ffi.rs (100%) rename {src/rust/mw_log_subscriber => score/mw/log/rust/mw_logger}/src/rust_cpp_log_adapter.cpp (100%) delete mode 100644 src/BUILD delete mode 100644 src/cpp/.gitkeep diff --git a/BUILD b/BUILD index b1c9667..c214ee9 100644 --- a/BUILD +++ b/BUILD @@ -25,7 +25,6 @@ copyright_checker( "docs", "examples", "score", - "src", "tests", "//:.bazelrc", "//:BUILD", diff --git a/README.md b/README.md index 0dd2bd3..306317c 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ It provides a **standardized project structure**, ensuring best practices for: | File/Folder | Description | | ----------------------------------- | ------------------------------------------------- | | `README.md` | Short description & build instructions | -| `src/` | Source files for the module | +| `score/` | Source files for the module | | `tests/` | Unit tests (UT) and integration tests (IT) | | `examples/` | Example files used for guidance | | `docs/` | Documentation (Doxygen for C++ / mdBook for Rust) | @@ -47,15 +47,15 @@ cd YOUR_PROJECT To build all targets of the module the following command can be used: ```sh -bazel build //src/... +bazel build //score/... ``` This command will instruct Bazel to build all targets that are under Bazel -package `src/`. The ideal solution is to provide single target that builds +package `score/`. The ideal solution is to provide single target that builds artifacts, for example: ```sh -bazel build //src/:release_artifacts +bazel build //score/:release_artifacts ``` where `:release_artifacts` is filegroup target that collects all release diff --git a/docs/index.rst b/docs/index.rst index f8e53da..2667bd1 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -44,7 +44,7 @@ Project Layout The module template includes the following top-level structure: -- `src/`: Main C++/Rust sources +- `score/`: Main C++/Rust sources - `tests/`: Unit and integration tests - `examples/`: Usage examples - `docs/`: Documentation using `docs-as-code` @@ -57,7 +57,7 @@ To build the module: .. code-block:: bash - bazel build //src/... + bazel build //score/... To run tests: diff --git a/src/rust/mw_log_subscriber/BUILD b/score/mw/log/rust/mw_logger/BUILD similarity index 93% rename from src/rust/mw_log_subscriber/BUILD rename to score/mw/log/rust/mw_logger/BUILD index 285d303..acc2499 100644 --- a/src/rust/mw_log_subscriber/BUILD +++ b/score/mw/log/rust/mw_logger/BUILD @@ -25,9 +25,9 @@ cc_library( ) rust_library( - name = "mw_log_subscriber", + name = "mw_logger", srcs = glob(["src/**/*.rs"]), - crate_name = "mw_log_subscriber", + crate_name = "mw_logger", edition = "2021", visibility = ["//visibility:public"], deps = [ @@ -50,7 +50,7 @@ rust_binary( ], visibility = ["//visibility:public"], deps = [ - ":mw_log_subscriber", + ":mw_logger", "@score_baselibs//score/mw/log/rust:log", ], ) diff --git a/src/rust/mw_log_subscriber/examples/config/logging.json b/score/mw/log/rust/mw_logger/examples/config/logging.json similarity index 100% rename from src/rust/mw_log_subscriber/examples/config/logging.json rename to score/mw/log/rust/mw_logger/examples/config/logging.json diff --git a/src/rust/mw_log_subscriber/examples/main.rs b/score/mw/log/rust/mw_logger/examples/main.rs similarity index 98% rename from src/rust/mw_log_subscriber/examples/main.rs rename to score/mw/log/rust/mw_logger/examples/main.rs index d31299e..a02bfee 100644 --- a/src/rust/mw_log_subscriber/examples/main.rs +++ b/score/mw/log/rust/mw_logger/examples/main.rs @@ -14,7 +14,7 @@ use std::path::PathBuf; use log::{debug, error, info, trace, warn}; -use mw_log_subscriber::MwLoggerBuilder; +use mw_logger::MwLoggerBuilder; fn main() { //Setup for example using config file diff --git a/src/rust/mw_log_subscriber/src/lib.rs b/score/mw/log/rust/mw_logger/src/lib.rs similarity index 100% rename from src/rust/mw_log_subscriber/src/lib.rs rename to score/mw/log/rust/mw_logger/src/lib.rs diff --git a/src/rust/mw_log_subscriber/src/mw_log_ffi.rs b/score/mw/log/rust/mw_logger/src/mw_log_ffi.rs similarity index 100% rename from src/rust/mw_log_subscriber/src/mw_log_ffi.rs rename to score/mw/log/rust/mw_logger/src/mw_log_ffi.rs diff --git a/src/rust/mw_log_subscriber/src/rust_cpp_log_adapter.cpp b/score/mw/log/rust/mw_logger/src/rust_cpp_log_adapter.cpp similarity index 100% rename from src/rust/mw_log_subscriber/src/rust_cpp_log_adapter.cpp rename to score/mw/log/rust/mw_logger/src/rust_cpp_log_adapter.cpp diff --git a/scripts/generate_rust_analyzer_support.sh b/scripts/generate_rust_analyzer_support.sh index 2f78bc7..2a67257 100755 --- a/scripts/generate_rust_analyzer_support.sh +++ b/scripts/generate_rust_analyzer_support.sh @@ -3,4 +3,4 @@ set -e # Manual targets are not take into account, must be set explicitly -bazel run @rules_rust//tools/rust_analyzer:gen_rust_project -- "@//examples/..." "@//src/..." "@//tests/..." +bazel run @rules_rust//tools/rust_analyzer:gen_rust_project -- "@//examples/..." "@//score/..." "@//tests/..." diff --git a/src/BUILD b/src/BUILD deleted file mode 100644 index e69de29..0000000 diff --git a/src/cpp/.gitkeep b/src/cpp/.gitkeep deleted file mode 100644 index e69de29..0000000 From cddacdca53495943046c48083c2ac861494c50af Mon Sep 17 00:00:00 2001 From: Raghavendra Maddikery Date: Tue, 20 Jan 2026 16:41:52 +0100 Subject: [PATCH 19/34] Brings Datarouter changes for reference_platform integration (#31) * Project import generated by Copybara. GIT_ORIGIN_SPP_REV_ID: 8a2b2eea1f568d6202ab434296573a5e11e2757a * Skip test if feature is disabled * Adds missing copyright headers --- score/datarouter/test/ut/ut_logging/test_socketserver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/score/datarouter/test/ut/ut_logging/test_socketserver.cpp b/score/datarouter/test/ut/ut_logging/test_socketserver.cpp index cbf3097..e7af5bd 100644 --- a/score/datarouter/test/ut/ut_logging/test_socketserver.cpp +++ b/score/datarouter/test/ut/ut_logging/test_socketserver.cpp @@ -11,13 +11,13 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -#include "applications/datarouter_feature_config.h" #include "daemon/socketserver.h" #include "logparser/logparser.h" #include "score/os/mocklib/unistdmock.h" #include "score/mw/log/configuration/invconfig_mock.h" #include "score/datarouter/datarouter/data_router.h" #include "score/datarouter/src/persistency/mock_persistent_dictionary.h" +#include "applications/datarouter_feature_config.h" #include "gmock/gmock.h" #include "gtest/gtest.h" From ab8b334533a47e4ddbccfa17ee0b8a1318579cf0 Mon Sep 17 00:00:00 2001 From: Raghavendra Maddikery Date: Wed, 21 Jan 2026 17:18:54 +0100 Subject: [PATCH 20/34] Project import generated by Copybara. (#32) GIT_ORIGIN_SPP_REV_ID: 4519f8b22ed384c3b787c0c0c9d0060b3e57b30e (cherry picked from commit d1ddad56ab0b41881a080f48c87cb2a28a79ff40) --- score/datarouter/test/ut/ut_logging/test_socketserver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/score/datarouter/test/ut/ut_logging/test_socketserver.cpp b/score/datarouter/test/ut/ut_logging/test_socketserver.cpp index e7af5bd..cbf3097 100644 --- a/score/datarouter/test/ut/ut_logging/test_socketserver.cpp +++ b/score/datarouter/test/ut/ut_logging/test_socketserver.cpp @@ -11,13 +11,13 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ +#include "applications/datarouter_feature_config.h" #include "daemon/socketserver.h" #include "logparser/logparser.h" #include "score/os/mocklib/unistdmock.h" #include "score/mw/log/configuration/invconfig_mock.h" #include "score/datarouter/datarouter/data_router.h" #include "score/datarouter/src/persistency/mock_persistent_dictionary.h" -#include "applications/datarouter_feature_config.h" #include "gmock/gmock.h" #include "gtest/gtest.h" From d710605254357c702918c56d08330e64373fbf72 Mon Sep 17 00:00:00 2001 From: Raghavendra Maddikery Date: Thu, 22 Jan 2026 09:20:08 +0100 Subject: [PATCH 21/34] Updates CODEOWNERS for datarouter and mw/log directories (#29) - Assign codeowners to directories based on initial contributions --- .github/CODEOWNERS | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f0adbb6..2d4c33d 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,2 +1,13 @@ # default owners -* @antonkri @rmaddikery @hoppe-and-dreams @mobileinfo @andreapefe @RSingh1511 @Y-Vaishnavi @pawelrutkaq @arkjedrz @arsibo +* @antonkri @pawelrutkaq @arkjedrz @arsibo + +# datarouter owners +/score/datarouter @rmaddikery @hoppe-and-dreams @mobileinfo @andreapefe @RSingh1511 @Y-Vaishnavi + +# mw/log owners +/score/mw/common_features.bzl @rmaddikery @hoppe-and-dreams @mobileinfo @andreapefe @RSingh1511 @Y-Vaishnavi + +/score/mw/log @rmaddikery @hoppe-and-dreams @mobileinfo @andreapefe @RSingh1511 @Y-Vaishnavi + +# mw/log rust mw_logger owners (more specific rule overrides general rule above) +/score/mw/log/rust/mw_logger @pawelrutkaq @arkjedrz From 0ec81e36d45abe714180eecd7a86726147bfa07a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20J=C4=99drzejewski?= <32277297+arkjedrz@users.noreply.github.com> Date: Thu, 22 Jan 2026 15:06:55 +0100 Subject: [PATCH 22/34] log: C++-based backend implementation (#14) * log: C++-based backend implementation `mw_log` backend implementation using `baselibs`. * log: post-review fixes - Improve `Context`: - Use it consistenly internally - no allocs. - Make use of `From` traits (to and from `&str`). - Check layout on every logger builder. - Cheap and rare operation, no deps to `std::sync` required. - Improve docs and add safety notes. - `LogMessage`: - Merge `ScoreLoggerWriter` and `LogStream` - less layers. - Remove `score_logger_writer.rs`. - Add `#[inline]` where applicable. - Prevent operation in erroneous state (e.g., when null returned). - Less branching. - Move `Send` and `Sync` where actually needed (to `Recorder`). - Improve example. --- MODULE.bazel | 4 + score/mw/log/rust/mw_logger/BUILD | 56 -- score/mw/log/rust/mw_logger/examples/main.rs | 74 --- score/mw/log/rust/mw_logger/src/lib.rs | 213 ------- score/mw/log/rust/mw_logger/src/mw_log_ffi.rs | 66 -- .../mw_logger/src/rust_cpp_log_adapter.cpp | 105 ---- score/mw/log/rust/score_log_bridge/BUILD | 111 ++++ .../examples/config/logging.json | 4 +- .../rust/score_log_bridge/examples/main.rs | 87 +++ .../log/rust/score_log_bridge/src/adapter.cpp | 248 ++++++++ score/mw/log/rust/score_log_bridge/src/ffi.rs | 564 ++++++++++++++++++ score/mw/log/rust/score_log_bridge/src/lib.rs | 23 + .../rust/score_log_bridge/src/score_logger.rs | 346 +++++++++++ 13 files changed, 1385 insertions(+), 516 deletions(-) delete mode 100644 score/mw/log/rust/mw_logger/BUILD delete mode 100644 score/mw/log/rust/mw_logger/examples/main.rs delete mode 100644 score/mw/log/rust/mw_logger/src/lib.rs delete mode 100644 score/mw/log/rust/mw_logger/src/mw_log_ffi.rs delete mode 100644 score/mw/log/rust/mw_logger/src/rust_cpp_log_adapter.cpp create mode 100644 score/mw/log/rust/score_log_bridge/BUILD rename score/mw/log/rust/{mw_logger => score_log_bridge}/examples/config/logging.json (62%) create mode 100644 score/mw/log/rust/score_log_bridge/examples/main.rs create mode 100644 score/mw/log/rust/score_log_bridge/src/adapter.cpp create mode 100644 score/mw/log/rust/score_log_bridge/src/ffi.rs create mode 100644 score/mw/log/rust/score_log_bridge/src/lib.rs create mode 100644 score/mw/log/rust/score_log_bridge/src/score_logger.rs diff --git a/MODULE.bazel b/MODULE.bazel index f048bc3..b359b9a 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -120,3 +120,7 @@ git_override( commit = "3c65b223e9f516f95935bb4cd2e83d6088ca016f", remote = "https://github.com/eclipse-score/baselibs.git", ) + +# Rust dependencies + +bazel_dep(name = "score_baselibs_rust", version = "0.0.3") diff --git a/score/mw/log/rust/mw_logger/BUILD b/score/mw/log/rust/mw_logger/BUILD deleted file mode 100644 index acc2499..0000000 --- a/score/mw/log/rust/mw_logger/BUILD +++ /dev/null @@ -1,56 +0,0 @@ -# ******************************************************************************* -# 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("@rules_rust//cargo:defs.bzl", "cargo_build_script") -load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library") - -cc_library( - name = "librust_cpp_log_adapter", - srcs = ["src/rust_cpp_log_adapter.cpp"], - visibility = ["//visibility:private"], - deps = [ - "//score/mw/log/detail/common:recorder_factory", - "@score_baselibs//score/mw/log:frontend", - ], -) - -rust_library( - name = "mw_logger", - srcs = glob(["src/**/*.rs"]), - crate_name = "mw_logger", - edition = "2021", - visibility = ["//visibility:public"], - deps = [ - ":librust_cpp_log_adapter", - "@score_baselibs//score/mw/log/rust:log", - ], -) - -rust_binary( - name = "example", - srcs = ["examples/main.rs"], - data = [ - "examples/config/logging.json", - ], - edition = "2021", - rustc_flags = [ - "-Clink-arg=-lstdc++", - "-Clink-arg=-lm", - "-Clink-arg=-lc", - ], - visibility = ["//visibility:public"], - deps = [ - ":mw_logger", - "@score_baselibs//score/mw/log/rust:log", - ], -) diff --git a/score/mw/log/rust/mw_logger/examples/main.rs b/score/mw/log/rust/mw_logger/examples/main.rs deleted file mode 100644 index a02bfee..0000000 --- a/score/mw/log/rust/mw_logger/examples/main.rs +++ /dev/null @@ -1,74 +0,0 @@ -// -// 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 -// -// -// SPDX-License-Identifier: Apache-2.0 -// - -use std::path::PathBuf; - -use log::{debug, error, info, trace, warn}; -use mw_logger::MwLoggerBuilder; - -fn main() { - //Setup for example using config file - let path = PathBuf::from(std::env::current_dir().unwrap()) - .join(file!()) - .parent() - .unwrap() - .join("config") - .join("logging.json"); - - std::env::set_var("MW_LOG_CONFIG_FILE", path.as_os_str()); - - // Just initialize and set as default logger - MwLoggerBuilder::new().set_as_default_logger::(); - - trace!("This is a trace log"); - debug!("This is a debug log"); - error!("This is an error log"); - info!("This is an info log"); - warn!("This is a warn log"); - - error!( - "This is an log that will be trimmed: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb - ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc - ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd - eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee - fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - ggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg - END MARKER NOT VISIBLE" - ); - - error!( - "This is an log that will be trimmed {} {} {} {} {} {} {}. END MARKER NOT VISIBLE", - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", - "ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", - "ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", - "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg" - ); - - // Using logger instance with context - let logger = MwLoggerBuilder::new() - .with_context("ALFA") - .build::(); - - trace!( - logger : logger, - "This is a trace log" - ); - debug!(logger : logger, "This is a debug log"); - error!(logger : logger, "This is an error log"); - info!(logger : logger, "This is an info log"); - warn!(logger : logger, "This is a warn log"); -} diff --git a/score/mw/log/rust/mw_logger/src/lib.rs b/score/mw/log/rust/mw_logger/src/lib.rs deleted file mode 100644 index 41aecfe..0000000 --- a/score/mw/log/rust/mw_logger/src/lib.rs +++ /dev/null @@ -1,213 +0,0 @@ -// -// 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 -// -// -// SPDX-License-Identifier: Apache-2.0 -// - -mod mw_log_ffi; - -use crate::mw_log_ffi::*; - -use core::ffi::c_char; -use core::fmt::{self, Write}; -use log::{Level, Log, Metadata, Record}; -use std::ffi::CString; -use std::mem::MaybeUninit; - -const MSG_SIZE: usize = 512; - -/// Builder for the MwLogger -pub struct MwLoggerBuilder { - context: Option, -} - -impl MwLoggerBuilder { - pub fn new() -> Self { - Self { context: None } - } - - /// Builds the MwLogger with the specified context and configuration and returns it. - pub fn build(self) -> MwLogger { - let context_cstr = self.context.unwrap_or(CString::new("DFLT").unwrap()); - let c_logger_ptr = unsafe { mw_log_create_logger(context_cstr.as_ptr().cast::()) }; - MwLogger { - ptr: c_logger_ptr, - log_fn: log::, - } - } - - /// Builds and sets the MwLogger as the default logger with the specified configuration. - pub fn set_as_default_logger(self) { - let logger = self.build::(); - log::set_max_level(mw_log_logger_level(logger.ptr)); - log::set_boxed_logger(Box::new(logger)) - .expect("Failed to initialize MwLogger as default logger - logger may already be set"); - } - - /// Sets the context for currently build logger. - pub fn with_context(mut self, context: &str) -> Self { - self.context = Some(CString::new(context).expect( - "Failed to create CString: - input contains null bytes", - )); - self - } -} - -/// A simple buffer writer that implements `core::fmt::Write` -/// and writes into a fixed-size(`BUF_SIZE) buffer. -/// Used in `Log` implementation to format log messages -/// before passing them to the underlying C++ logger. -struct BufWriter { - buf: [MaybeUninit; BUF_SIZE], - pos: usize, -} - -impl BufWriter { - fn new() -> Self { - Self { - buf: [MaybeUninit::uninit(); BUF_SIZE], - pos: 0, - } - } - - /// Returns a slice to filled part of buffer. This is not null-terminated. - fn as_slice(&self) -> &[c_char] { - // SAFETY: We only expose already initialized part of the buffer - unsafe { core::slice::from_raw_parts(self.buf.as_ptr().cast::(), self.len()) } - } - - /// Returns the current length of the filled part of the buffer. - fn len(&self) -> usize { - self.pos - } - - /// Reverts the current position (consumes) by `cnt` bytes, saturating at 0. - fn revert_pos(&mut self, cnt: usize) { - self.pos = self.pos.saturating_sub(cnt); - } - - // Finds the right index (around requested `index`) to trim utf8 string to a valid char boundary - fn floor_char_boundary(view: &str, index: usize) -> usize { - if index >= view.len() { - view.len() - } else { - let lower_bound = index.saturating_sub(3); - let new_index = view.as_bytes()[lower_bound..=index] - .iter() - .rposition(|b| Self::is_utf8_char_boundary(*b)); - - // SAFETY: we know that the character boundary will be within four bytes - unsafe { lower_bound + new_index.unwrap_unchecked() } - } - } - - const fn is_utf8_char_boundary(i: u8) -> bool { - // This is bit magic equivalent to: b < 128 || b >= 192 - (i as i8) >= -0x40 - } -} - -impl Write for BufWriter { - fn write_str(&mut self, s: &str) -> fmt::Result { - let bytes = s.as_bytes(); - let to_write = bytes.len().min(self.buf.len() - self.pos - 1); // Cutting write, instead error when we don't fit - let bounded_to_write = Self::floor_char_boundary(s, to_write); - - if bounded_to_write == 0 { - return Err(fmt::Error); - } - - let dest = self.buf[self.pos..self.pos + bounded_to_write] - .as_mut_ptr() - .cast::(); - - unsafe { core::ptr::copy_nonoverlapping(bytes.as_ptr(), dest, bounded_to_write) }; - - self.pos += bounded_to_write; - Ok(()) - } -} - -pub struct MwLogger { - ptr: *const Logger, - log_fn: fn(&mut BufWriter, &Record), -} - -// SAFETY: The underlying C++ logger is known to be safe to change thread -unsafe impl Send for MwLogger {} - -// SAFETY: The underlying C++ logger is known to be thread-safe. -unsafe impl Sync for MwLogger {} - -impl MwLogger { - fn write_log(&self, level: Level, msg: &BufWriter) { - let slice = msg.as_slice(); - - unsafe { - match level { - Level::Error => mw_log_error_logger(self.ptr, slice.as_ptr(), slice.len() as u32), - Level::Warn => mw_log_warn_logger(self.ptr, slice.as_ptr(), slice.len() as u32), - Level::Info => mw_log_info_logger(self.ptr, slice.as_ptr(), slice.len() as u32), - Level::Debug => mw_log_debug_logger(self.ptr, slice.as_ptr(), slice.len() as u32), - Level::Trace => mw_log_verbose_logger(self.ptr, slice.as_ptr(), slice.len() as u32), - } - } - } -} - -impl Log for MwLogger { - fn enabled(&self, metadata: &Metadata) -> bool { - mw_log_is_log_level_enabled(self.ptr, metadata.level()) - } - fn log(&self, record: &Record) { - if !self.enabled(record.metadata()) { - return; - } - - let mut msg_writer = BufWriter::::new(); - (self.log_fn)(&mut msg_writer, record); - - self.write_log(record.level(), &msg_writer); - } - - fn flush(&self) { - // No-op for this logger, as it does not buffer logs - } -} - -fn log( - msg_writer: &mut BufWriter, - record: &Record, -) { - if SHOW_FILE || SHOW_LINE || SHOW_MODULE { - let _ = write!(msg_writer, "["); - if SHOW_MODULE { - if let Some(module) = record.module_path() { - let _ = write!(msg_writer, "{}:", module); - } - } - if SHOW_FILE { - if let Some(file) = record.file() { - let _ = write!(msg_writer, "{}:", file); - } - } - if SHOW_LINE { - if let Some(line) = record.line() { - let _ = write!(msg_writer, "{}:", line); - } - } - - msg_writer.revert_pos(1); - let _ = write!(msg_writer, "] "); - } - - let _ = msg_writer.write_fmt(*record.args()); -} diff --git a/score/mw/log/rust/mw_logger/src/mw_log_ffi.rs b/score/mw/log/rust/mw_logger/src/mw_log_ffi.rs deleted file mode 100644 index 339fc84..0000000 --- a/score/mw/log/rust/mw_logger/src/mw_log_ffi.rs +++ /dev/null @@ -1,66 +0,0 @@ -// -// 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 -// -// -// SPDX-License-Identifier: Apache-2.0 -// - -use core::ffi::c_char; - -use log::{Level, LevelFilter}; - -// Opaque type representing the C++ logger ptr -#[repr(C)] -pub(crate) struct Logger { - _private: [u8; 0], // Opaque -} - -pub(crate) fn mw_log_logger_level(logger: *const Logger) -> LevelFilter { - let level = unsafe { mw_log_logger_level_internal(logger) }; - log_level_from_ffi(level) -} - -pub(crate) fn mw_log_is_log_level_enabled(logger: *const Logger, level: Level) -> bool { - let level_byte = match level { - Level::Error => 0x02, - Level::Warn => 0x03, - Level::Info => 0x04, - Level::Debug => 0x05, - Level::Trace => 0x06, - }; - unsafe { mw_log_is_log_level_enabled_internal(logger, level_byte) } -} - -/// Get the max log level from C++ as a LevelFilter directly -fn log_level_from_ffi(level: u8) -> LevelFilter { - match level { - 0x00 => LevelFilter::Off, - 0x01 => LevelFilter::Error, // Currently Fatal treated as Error - 0x02 => LevelFilter::Error, - 0x03 => LevelFilter::Warn, - 0x04 => LevelFilter::Info, - 0x05 => LevelFilter::Debug, - 0x06 => LevelFilter::Trace, // Verbose is Trace - _ => LevelFilter::Info, // fallback - } -} - -extern "C" { - - pub(crate) fn mw_log_create_logger(context: *const c_char) -> *mut Logger; - pub(crate) fn mw_log_error_logger(logger: *const Logger, message: *const c_char, len: u32); - pub(crate) fn mw_log_warn_logger(logger: *const Logger, message: *const c_char, len: u32); - pub(crate) fn mw_log_info_logger(logger: *const Logger, message: *const c_char, len: u32); - pub(crate) fn mw_log_debug_logger(logger: *const Logger, message: *const c_char, len: u32); - pub(crate) fn mw_log_verbose_logger(logger: *const Logger, message: *const c_char, len: u32); - - fn mw_log_is_log_level_enabled_internal(logger: *const Logger, level: u8) -> bool; - fn mw_log_logger_level_internal(logger: *const Logger) -> u8; - -} diff --git a/score/mw/log/rust/mw_logger/src/rust_cpp_log_adapter.cpp b/score/mw/log/rust/mw_logger/src/rust_cpp_log_adapter.cpp deleted file mode 100644 index 22ec0ed..0000000 --- a/score/mw/log/rust/mw_logger/src/rust_cpp_log_adapter.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/******************************************************************************** - * 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/logging.h" -#include "score/mw/log/configuration/configuration.h" -#include "score/mw/log/logger.h" -#include "score/mw/log/log_level.h" - -namespace score::mw::log -{ - extern "C" - { - - Logger *mw_log_create_logger(const char *context) - { - return &CreateLogger(context); - } - - bool mw_log_is_log_level_enabled_internal(const Logger *logger, uint8_t level) - { - return logger->IsLogEnabled(GetLogLevelFromU8(level)); - } - - void mw_log_fatal_logger(const Logger *logger, const char *message, uint32_t size) - { - logger->LogFatal() << LogString{message, size}; - } - - void mw_log_error_logger(const Logger *logger, const char *message, uint32_t size) - { - logger->LogError() << LogString{message, size}; - } - - void mw_log_warn_logger(const Logger *logger, const char *message, uint32_t size) - { - logger->LogWarn() << LogString{message, size}; - } - - void mw_log_info_logger(const Logger *logger, const char *message, uint32_t size) - { - logger->LogInfo() << LogString{message, size}; - } - - void mw_log_debug_logger(const Logger *logger, const char *message, uint32_t size) - { - logger->LogDebug() << LogString{message, size}; - } - - void mw_log_verbose_logger(const Logger *logger, const char *message, uint32_t size) - { - logger->LogVerbose() << LogString{message, size}; - } - - uint8_t mw_log_logger_level_internal(const Logger *logger) - { - // TODO: This is adapter code, as there seems to be no way to get log level for Logger - if (logger->IsLogEnabled(LogLevel::kInfo)) - { - // Between Verbose, Debug, Info - if (logger->IsLogEnabled(LogLevel::kDebug)) - { - if (logger->IsLogEnabled(LogLevel::kVerbose)) - { - return static_cast(LogLevel::kVerbose); - } - - return static_cast(LogLevel::kDebug); - } - - return static_cast(LogLevel::kInfo); - } - else - { - // Lower half: Warn, Error, Fatal - if (logger->IsLogEnabled(LogLevel::kError)) - { - if (logger->IsLogEnabled(LogLevel::kWarn)) - { - return static_cast(LogLevel::kWarn); - } - - return static_cast(LogLevel::kError); - } - - if (logger->IsLogEnabled(LogLevel::kFatal)) - { - return static_cast(LogLevel::kFatal); - } - } - - // fallback - return static_cast(LogLevel::kOff); - } - } -} // namespace score::mw::log diff --git a/score/mw/log/rust/score_log_bridge/BUILD b/score/mw/log/rust/score_log_bridge/BUILD new file mode 100644 index 0000000..31523e1 --- /dev/null +++ b/score/mw/log/rust/score_log_bridge/BUILD @@ -0,0 +1,111 @@ +# ******************************************************************************* +# 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("@rules_cc//cc:cc_library.bzl", "cc_library") +load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library", "rust_test") + +config_setting( + name = "x86_64-linux", + constraint_values = [ + "@platforms//cpu:x86_64", + "@platforms//os:linux", + ], +) + +config_setting( + name = "arm64-qnx", + constraint_values = [ + "@platforms//cpu:arm64", + "@platforms//os:qnx", + ], +) + +cc_library( + name = "adapter", + srcs = ["src/adapter.cpp"], + # C++/Rust interface objects must share layout. + defines = select({ + ":x86_64-linux": ["x86_64_linux"], + ":arm64-qnx": ["arm64_qnx"], + "//conditions:default": [], + }), + visibility = ["//visibility:private"], + deps = [ + "//score/mw/log/detail/common:recorder_factory", + "@score_baselibs//score/mw/log:frontend", + ], +) + +rust_library( + name = "score_log_bridge", + srcs = glob(["src/**/*.rs"]), + # C++/Rust interface objects must share layout. + crate_features = select({ + ":x86_64-linux": ["x86_64_linux"], + ":arm64-qnx": ["arm64_qnx"], + "//conditions:default": [], + }), + edition = "2021", + visibility = ["//visibility:public"], + deps = [ + ":adapter", + "@score_baselibs_rust//src/log/score_log", + ], +) + +RUSTC_FLAGS = select({ + "@platforms//os:qnx": [ + "-Clink-arg=-lc++", + "-Clink-arg=-lm", + ], + "//conditions:default": [ + "-Clink-arg=-lstdc++", + "-Clink-arg=-lm", + "-Clink-arg=-lc", + ], +}) + +rust_test( + name = "tests", + crate = "score_log_bridge", + crate_features = select({ + ":x86_64-linux": ["x86_64_linux"], + ":arm64-qnx": ["arm64_qnx"], + "//conditions:default": [], + }), + edition = "2021", + rustc_flags = RUSTC_FLAGS, + tags = [ + "unit_tests", + "ut", + ], + deps = [ + "@score_crates//:libc", + "@score_crates//:tempfile", + ], +) + +rust_binary( + name = "example", + srcs = ["examples/main.rs"], + data = [ + "examples/config/logging.json", + ], + edition = "2021", + rustc_flags = RUSTC_FLAGS, + visibility = ["//visibility:public"], + deps = [ + ":score_log_bridge", + "@score_baselibs_rust//src/log/score_log", + ], +) diff --git a/score/mw/log/rust/mw_logger/examples/config/logging.json b/score/mw/log/rust/score_log_bridge/examples/config/logging.json similarity index 62% rename from score/mw/log/rust/mw_logger/examples/config/logging.json rename to score/mw/log/rust/score_log_bridge/examples/config/logging.json index cf4f333..ac97a33 100644 --- a/score/mw/log/rust/mw_logger/examples/config/logging.json +++ b/score/mw/log/rust/score_log_bridge/examples/config/logging.json @@ -1,6 +1,6 @@ { - "appId": "JSON", - "appDesc": "JSON example programs", + "appId": "EXMP", + "appDesc": "Logger example", "logMode" : "kConsole", "logLevel": "kVerbose", "logLevelThresholdConsole": "kInfo" diff --git a/score/mw/log/rust/score_log_bridge/examples/main.rs b/score/mw/log/rust/score_log_bridge/examples/main.rs new file mode 100644 index 0000000..8c1c928 --- /dev/null +++ b/score/mw/log/rust/score_log_bridge/examples/main.rs @@ -0,0 +1,87 @@ +// +// 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 +// +// +// SPDX-License-Identifier: Apache-2.0 +// + +use score_log::{debug, error, fatal, info, trace, warn, Log}; +use score_log_bridge::ScoreLoggerBuilder; +use std::path::PathBuf; + +fn main() { + // Setup for example using config file. + let config_path = PathBuf::from(std::env::current_dir().unwrap()) + .join(file!()) + .parent() + .unwrap() + .join("config") + .join("logging.json"); + + // Initialize `ScoreLogger` as a default logger. + ScoreLoggerBuilder::new() + .show_module(false) + .show_file(true) + .show_line(false) + .config(config_path) + .set_as_default_logger(); + + // Regular log usage. + trace!("This is a trace log - hidden"); + debug!("This is a debug log - hidden"); + info!("This is an info log"); + warn!("This is a warn log"); + error!("This is an error log"); + fatal!("This is a fatal log"); + + // Log with modified context. + trace!(context: "EX1", "This is a trace log - hidden"); + debug!(context: "EX1", "This is a debug log - hidden"); + info!(context: "EX1", "This is an info log"); + warn!(context: "EX1", "This is a warn log"); + error!(context: "EX1", "This is an error log"); + fatal!(context: "EX1", "This is a fatal log"); + + // Log with numeric values. + let x1 = 123.4; + let x2 = 111; + let x3 = true; + let x4 = -0x3Fi8; + error!( + "This is an error log with numeric values: {} {} {} {:x}", + x1, x2, x3, x4, + ); + + // Use logger instance with modified context. + let logger = ScoreLoggerBuilder::new() + .context("ALFA") + .show_module(false) + .show_file(true) + .show_line(false) + .build(); + + // Log with provided logger. + trace!( + logger: logger, + "This is a trace log - hidden" + ); + debug!(logger: logger, "This is a debug log - hidden"); + info!(logger: logger, "This is an info log"); + warn!(logger: logger, "This is a warn log"); + error!(logger: logger, "This is an error log"); + fatal!(logger: logger, "This is an fatal log"); + + // Log with provided logger and modified context. + trace!(logger: logger, context: "EX2", "This is a trace log - hidden"); + debug!(logger: logger, context: "EX2", "This is a debug log - hidden"); + info!(logger: logger, context: "EX2", "This is an info log"); + warn!(logger: logger, context: "EX2", "This is a warn log"); + error!(logger: logger, context: "EX2", "This is an error log"); + fatal!(logger: logger, context: "EX2", "This is an fatal log"); +} diff --git a/score/mw/log/rust/score_log_bridge/src/adapter.cpp b/score/mw/log/rust/score_log_bridge/src/adapter.cpp new file mode 100644 index 0000000..30e01a8 --- /dev/null +++ b/score/mw/log/rust/score_log_bridge/src/adapter.cpp @@ -0,0 +1,248 @@ +/******************************************************************************** + * 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/runtime.h" +#include "score/mw/log/slot_handle.h" + +using namespace score::mw::log; +using namespace score::mw::log::detail; + +// Verify configuration at compile time. +// `SlotHandle` is expected to be allocated on stack to reduce performance overhead. +// Size and alignment of `SlotHandle` (C++) and `SlotHandleStorage` (Rust) must match. +// Those parameters must be: +// - managed by build system (using defines and features) +// - cross-checked between `ffi.rs` and `adapter.cpp` +#if defined(x86_64_linux) || defined(arm64_qnx) +// Expected size and alignment of `SlotHandle`. +static_assert(sizeof(SlotHandle) == 24); +static_assert(alignof(SlotHandle) == 8); +#else +#error "Unknown configuration, unable to check layout" +#endif + +extern "C" { +/// @brief Get current recorder from runtime. +/// @return Current recorder. +Recorder* recorder_get() { return &Runtime::GetRecorder(); } + +/// @brief Start recording log message. +/// @param recorder Recorder. +/// @param context Message context name. +/// @param context_size Message context name size. +/// @param log_level Message log level. +/// @param slot `SlotHandle`-sized buffer. +/// @return `slot` if acquired, `nullptr` otherwise. +SlotHandle* recorder_start(Recorder* recorder, const char* context, size_t context_size, + LogLevel log_level, SlotHandle* slot) { + auto start_result{recorder->StartRecord(std::string_view{context, context_size}, log_level)}; + if (start_result) { + return new (slot) SlotHandle{*start_result}; + } else { + return nullptr; + } +} + +/// @brief Get current log level for provided context. +/// @param recorder Recorder. +/// @param context Message context name. +/// @param context_size Message context name size. +/// @return Current log level. +LogLevel recorder_log_level(const Recorder* recorder, const char* context, size_t context_size) { + auto first{static_cast(LogLevel::kOff)}; + auto last{static_cast(LogLevel::kVerbose)}; + // Reversed order - `kOff` always seem to report true. + for (uint8_t i{last}; i > first; --i) { + auto current = static_cast(i); + if (recorder->IsLogEnabled(current, context)) { + return current; + } + } + + // Fall-back. + return LogLevel::kOff; +} + +/// @brief Stop recording log message. +/// @param recorder Recorder. +/// @param slot Acquired slot. +void recorder_stop(Recorder* recorder, SlotHandle* slot) { recorder->StopRecord(*slot); } + +/// @brief Add bool value to message. +/// @param recorder Recorder. +/// @param slot Acquired slot. +/// @param value Value. +void log_bool(Recorder* recorder, SlotHandle* slot, const bool* value) { + recorder->Log(*slot, *value); +} + +/// @brief Add f32 value to message. +/// @param recorder Recorder. +/// @param slot Acquired slot. +/// @param value Value. +void log_f32(Recorder* recorder, SlotHandle* slot, const float* value) { + recorder->Log(*slot, *value); +} + +/// @brief Add f64 value to message. +/// @param recorder Recorder. +/// @param slot Acquired slot. +/// @param value Value. +void log_f64(Recorder* recorder, SlotHandle* slot, const double* value) { + recorder->Log(*slot, *value); +} + +/// @brief Add string value to message. +/// @param recorder Recorder. +/// @param slot Acquired slot. +/// @param value Value. +void log_string(Recorder* recorder, SlotHandle* slot, const char* value, size_t size) { + recorder->Log(*slot, std::string_view{value, size}); +} + +/// @brief Add i8 value to message. +/// @param recorder Recorder. +/// @param slot Acquired slot. +/// @param value Value. +void log_i8(Recorder* recorder, SlotHandle* slot, const int8_t* value) { + recorder->Log(*slot, *value); +} + +/// @brief Add i16 value to message. +/// @param recorder Recorder. +/// @param slot Acquired slot. +/// @param value Value. +void log_i16(Recorder* recorder, SlotHandle* slot, const int16_t* value) { + recorder->Log(*slot, *value); +} + +/// @brief Add i32 value to message. +/// @param recorder Recorder. +/// @param slot Acquired slot. +/// @param value Value. +void log_i32(Recorder* recorder, SlotHandle* slot, const int32_t* value) { + recorder->Log(*slot, *value); +} + +/// @brief Add i64 value to message. +/// @param recorder Recorder. +/// @param slot Acquired slot. +/// @param value Value. +void log_i64(Recorder* recorder, SlotHandle* slot, const int64_t* value) { + recorder->Log(*slot, *value); +} + +/// @brief Add u8 value to message. +/// @param recorder Recorder. +/// @param slot Acquired slot. +/// @param value Value. +void log_u8(Recorder* recorder, SlotHandle* slot, const uint8_t* value) { + recorder->Log(*slot, *value); +} + +/// @brief Add u16 value to message. +/// @param recorder Recorder. +/// @param slot Acquired slot. +/// @param value Value. +void log_u16(Recorder* recorder, SlotHandle* slot, const uint16_t* value) { + recorder->Log(*slot, *value); +} + +/// @brief Add u32 value to message. +/// @param recorder Recorder. +/// @param slot Acquired slot. +/// @param value Value. +void log_u32(Recorder* recorder, SlotHandle* slot, const uint32_t* value) { + recorder->Log(*slot, *value); +} + +/// @brief Add u64 value to message. +/// @param recorder Recorder. +/// @param slot Acquired slot. +/// @param value Value. +void log_u64(Recorder* recorder, SlotHandle* slot, const uint64_t* value) { + recorder->Log(*slot, *value); +} + +/// @brief Add 8-bit binary value to message. +/// @param recorder Recorder. +/// @param slot Acquired slot. +/// @param value Value. +void log_bin8(Recorder* recorder, SlotHandle* slot, const uint8_t* value) { + recorder->Log(*slot, LogBin8{*value}); +} + +/// @brief Add 16-bit binary value to message. +/// @param recorder Recorder. +/// @param slot Acquired slot. +/// @param value Value. +void log_bin16(Recorder* recorder, SlotHandle* slot, const uint16_t* value) { + recorder->Log(*slot, LogBin16{*value}); +} + +/// @brief Add 32-bit binary value to message. +/// @param recorder Recorder. +/// @param slot Acquired slot. +/// @param value Value. +void log_bin32(Recorder* recorder, SlotHandle* slot, const uint32_t* value) { + recorder->Log(*slot, LogBin32{*value}); +} + +/// @brief Add 64-bit binary value to message. +/// @param recorder Recorder. +/// @param slot Acquired slot. +/// @param value Value. +void log_bin64(Recorder* recorder, SlotHandle* slot, const uint64_t* value) { + recorder->Log(*slot, LogBin64{*value}); +} + +/// @brief Add 8-bit hexadecimal value to message. +/// @param recorder Recorder. +/// @param slot Acquired slot. +/// @param value Value. +void log_hex8(Recorder* recorder, SlotHandle* slot, const uint8_t* value) { + recorder->Log(*slot, LogHex8{*value}); +} + +/// @brief Add 16-bit hexadecimal value to message. +/// @param recorder Recorder. +/// @param slot Acquired slot. +/// @param value Value. +void log_hex16(Recorder* recorder, SlotHandle* slot, const uint16_t* value) { + recorder->Log(*slot, LogHex16{*value}); +} + +/// @brief Add 32-bit hexadecimal value to message. +/// @param recorder Recorder. +/// @param slot Acquired slot. +/// @param value Value. +void log_hex32(Recorder* recorder, SlotHandle* slot, const uint32_t* value) { + recorder->Log(*slot, LogHex32{*value}); +} + +/// @brief Add 64-bit hexadecimal value to message. +/// @param recorder Recorder. +/// @param slot Acquired slot. +/// @param value Value. +void log_hex64(Recorder* recorder, SlotHandle* slot, const uint64_t* value) { + recorder->Log(*slot, LogHex64{*value}); +} + +/// @brief Get size of `SlotHandle`. +/// @return Size. +size_t slot_handle_size() { return sizeof(SlotHandle); } + +/// @brief Get alignment of `SlotHandle`. +/// @return Alignment. +size_t slot_handle_alignment() { return alignof(SlotHandle); } +} diff --git a/score/mw/log/rust/score_log_bridge/src/ffi.rs b/score/mw/log/rust/score_log_bridge/src/ffi.rs new file mode 100644 index 0000000..a896d89 --- /dev/null +++ b/score/mw/log/rust/score_log_bridge/src/ffi.rs @@ -0,0 +1,564 @@ +// +// 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 +// +// +// SPDX-License-Identifier: Apache-2.0 +// + +use core::alloc::Layout; +use core::cmp::min; +use core::ffi::c_char; +use core::mem::transmute; +use core::slice::from_raw_parts; +use score_log::fmt::{DisplayHint, Error, FormatSpec, Result as FmtResult, ScoreWrite}; + +/// Represents severity of a log message. +#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)] +#[repr(u8)] +pub enum LogLevel { + #[allow(dead_code)] + Off = 0x00, + Fatal = 0x01, + Error = 0x02, + Warn = 0x03, + Info = 0x04, + Debug = 0x05, + Verbose = 0x06, +} + +impl From for LogLevel { + fn from(score_log_level: score_log::Level) -> Self { + match score_log_level { + score_log::Level::Fatal => LogLevel::Fatal, + score_log::Level::Error => LogLevel::Error, + score_log::Level::Warn => LogLevel::Warn, + score_log::Level::Info => LogLevel::Info, + score_log::Level::Debug => LogLevel::Debug, + score_log::Level::Trace => LogLevel::Verbose, + } + } +} + +impl From for score_log::Level { + fn from(log_level: LogLevel) -> Self { + match log_level { + LogLevel::Fatal => score_log::Level::Fatal, + LogLevel::Error => score_log::Level::Error, + LogLevel::Warn => score_log::Level::Warn, + LogLevel::Info => score_log::Level::Info, + LogLevel::Debug => score_log::Level::Debug, + LogLevel::Verbose => score_log::Level::Trace, + _ => panic!("log level not supported"), + } + } +} + +impl From for score_log::LevelFilter { + fn from(level: LogLevel) -> score_log::LevelFilter { + match level { + LogLevel::Off => score_log::LevelFilter::Off, + LogLevel::Fatal => score_log::LevelFilter::Fatal, + LogLevel::Error => score_log::LevelFilter::Error, + LogLevel::Warn => score_log::LevelFilter::Warn, + LogLevel::Info => score_log::LevelFilter::Info, + LogLevel::Debug => score_log::LevelFilter::Debug, + LogLevel::Verbose => score_log::LevelFilter::Trace, + } + } +} + +/// Name of the context. +/// Max 4 bytes containing ASCII characters. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Context { + data: [c_char; 4], + size: usize, +} + +impl From<&str> for Context { + /// Create `Context` from `&str`. + /// + /// # Panics + /// + /// Method will panic if provided `&str` contains non-ASCII characters. + fn from(value: &str) -> Self { + // Disallow non-ASCII strings. + // ASCII characters are single byte in UTF-8. + assert!( + value.is_ascii(), + "provided context contains non-ASCII characters: {value}" + ); + + // Get number of characters. + let size = min(value.len(), 4); + + // Copy data into array. + let mut data = [0; _]; + // SAFETY: + // Copying is safe: + // - source is a `&str`. + // - destination is a stack-allocated array of known size. + // - number of bytes to copy is determined based on source. + unsafe { + core::ptr::copy_nonoverlapping(value.as_ptr(), data.as_mut_ptr().cast(), size); + } + + Self { data, size } + } +} + +impl From<&Context> for &str { + fn from(value: &Context) -> Self { + // SAFETY: + // ASCII characters (`c_char`) are compatible with single byte UTF-8 characters (`u8`). + // This function reinterprets data containing same information. + // `[c_char; _]` -> `* const u8` -> `&[u8]` -> `&str`. + + // Characters are reinterpreted from `c_char` (`i8`) to `u8`. + let data = value.data.as_ptr().cast(); + // Number of bytes is always bound to provided or max allowed size. + let size = min(value.size, 4); + unsafe { + // Create a slice from pointer and size. + let slice = from_raw_parts(data, size); + // Create a UTF-8 string from a slice. + str::from_utf8_unchecked(slice) + } + } +} + +/// Opaque type representing `Recorder`. +#[repr(C)] +struct RecorderPtr { + _private: [u8; 0], +} + +/// Recorder instance. +pub struct Recorder { + ptr: *mut RecorderPtr, +} + +impl Recorder { + /// Get recorder instance. + /// + /// # Panics + /// + /// Method panics if recorder was not properly initialized. + pub fn new() -> Self { + // Get recorder and check it's not null. + // SAFETY: Recorder must be available. Null indicates lack of proper initialization. + let ptr = unsafe { recorder_get() }; + assert!(!ptr.is_null(), "recorder is not properly initialized"); + Self { ptr } + } + + pub fn log_level(&self, context: &Context) -> LogLevel { + // SAFETY: FFI call. Validity of provided objects checked elsewhere. + unsafe { recorder_log_level(self.ptr, context.data.as_ptr(), context.size) } + } +} + +// SAFETY: +// `Runtime::GetRecorder()` is thread-safe in this use-case. +// Refer to `runtime.h` for more details. +unsafe impl Send for Recorder {} + +// SAFETY: +// `Runtime::GetRecorder()` is thread-safe in this use-case. +// Refer to `runtime.h` for more details. +unsafe impl Sync for Recorder {} + +/// Opaque storage type representing `SlotHandle`. +/// +/// `SlotHandle` is expected to be allocated on stack to reduce performance overhead. +/// Size and alignment of `SlotHandle` (C++) and `SlotHandleStorage` (Rust) must match. +/// Those parameters must be: +/// - managed by build system (using defines and features) +/// - cross-checked between `ffi.rs` and `adapter.cpp` +#[cfg(any(feature = "x86_64_linux", feature = "arm64_qnx"))] +#[repr(C, align(8))] +pub struct SlotHandleStorage { + _private: [u8; 24], +} + +impl SlotHandleStorage { + /// Returns an unsafe mutable pointer to this object. + pub fn as_mut_ptr(&mut self) -> *mut SlotHandleStorage { + self as *mut SlotHandleStorage + } + + /// Rust-side layout of this object. + pub fn layout_rust() -> Layout { + Layout::new::() + } + + /// C++-side layout of this object. + pub fn layout_cpp() -> Layout { + // SAFETY: parameter-less FFI calls. + let size = unsafe { slot_handle_size() }; + let align = unsafe { slot_handle_alignment() }; + Layout::from_size_align(size, align).expect("Invalid SlotHandle layout, size: {size}, alignment: {align}") + } +} + +impl Default for SlotHandleStorage { + /// Create storage for `SlotHandle`. + fn default() -> Self { + Self { _private: [0; _] } + } +} + +/// Single log message. +/// Adds values to the log message with selected formatting. +/// Message is flushed on drop. +pub struct LogMessage<'a> { + recorder: &'a Recorder, + slot: SlotHandleStorage, +} + +impl<'a> LogMessage<'a> { + pub fn new(recorder: &'a Recorder, context: &Context, log_level: LogLevel) -> Result { + // Start record. + // `SlotHandle` is allocated on stack. + let mut slot = SlotHandleStorage::default(); + // SAFETY: FFI call. + // Provided pointers are valid and checked elsewhere. + // `slot` is stack-allocated above. + // If result is null - logging is not performed. + let slot_result = unsafe { + recorder_start( + recorder.ptr, + context.data.as_ptr(), + context.size, + log_level, + slot.as_mut_ptr(), + ) + }; + + // Return without value if failed to acquire slot. + if slot_result.is_null() { + return Err(()); + } + + Ok(Self { recorder, slot }) + } +} + +impl ScoreWrite for LogMessage<'_> { + #[inline] + fn write_bool(&mut self, v: &bool, _spec: &FormatSpec) -> FmtResult { + // SAFETY: FFI call. Reference is reinterpreted as a pointer of the same type. + unsafe { log_bool(self.recorder.ptr, self.slot.as_mut_ptr(), v as *const _) }; + Ok(()) + } + + #[inline] + fn write_f32(&mut self, v: &f32, _spec: &FormatSpec) -> FmtResult { + // SAFETY: FFI call. Reference is reinterpreted as a pointer of the same type. + unsafe { log_f32(self.recorder.ptr, self.slot.as_mut_ptr(), v as *const _) }; + Ok(()) + } + + #[inline] + fn write_f64(&mut self, v: &f64, _spec: &FormatSpec) -> FmtResult { + // SAFETY: FFI call. Reference is reinterpreted as a pointer of the same type. + unsafe { log_f64(self.recorder.ptr, self.slot.as_mut_ptr(), v as *const _) }; + Ok(()) + } + + #[inline] + fn write_i8(&mut self, v: &i8, spec: &FormatSpec) -> FmtResult { + // SAFETY: FFI calls. Reference is reinterpreted as a pointer of the same type. + match spec.get_display_hint() { + DisplayHint::NoHint => unsafe { log_i8(self.recorder.ptr, self.slot.as_mut_ptr(), v as *const _) }, + DisplayHint::LowerHex | DisplayHint::UpperHex => { + // SAFETY: source and destination types are of the same length. + unsafe { + let v: &u8 = transmute(v); + log_hex8(self.recorder.ptr, self.slot.as_mut_ptr(), v as *const _); + } + }, + DisplayHint::Binary => { + // SAFETY: source and destination types are of the same length. + unsafe { + let v: &u8 = transmute(v); + log_bin8(self.recorder.ptr, self.slot.as_mut_ptr(), v as *const _); + } + }, + _ => return Err(Error), + } + Ok(()) + } + + #[inline] + fn write_i16(&mut self, v: &i16, spec: &FormatSpec) -> FmtResult { + // SAFETY: FFI calls. Reference is reinterpreted as a pointer of the same type. + match spec.get_display_hint() { + DisplayHint::NoHint => unsafe { log_i16(self.recorder.ptr, self.slot.as_mut_ptr(), v as *const _) }, + DisplayHint::LowerHex | DisplayHint::UpperHex => { + // SAFETY: source and destination types are of the same length. + unsafe { + let v: &u16 = transmute(v); + log_hex16(self.recorder.ptr, self.slot.as_mut_ptr(), v as *const _); + } + }, + DisplayHint::Binary => { + // SAFETY: source and destination types are of the same length. + unsafe { + let v: &u16 = transmute(v); + log_bin16(self.recorder.ptr, self.slot.as_mut_ptr(), v as *const _); + } + }, + _ => return Err(Error), + } + Ok(()) + } + + #[inline] + fn write_i32(&mut self, v: &i32, spec: &FormatSpec) -> FmtResult { + // SAFETY: FFI calls. Reference is reinterpreted as a pointer of the same type. + match spec.get_display_hint() { + DisplayHint::NoHint => unsafe { log_i32(self.recorder.ptr, self.slot.as_mut_ptr(), v as *const _) }, + DisplayHint::LowerHex | DisplayHint::UpperHex => { + // SAFETY: source and destination types are of the same length. + unsafe { + let v: &u32 = transmute(v); + log_hex32(self.recorder.ptr, self.slot.as_mut_ptr(), v as *const _); + } + }, + DisplayHint::Binary => { + // SAFETY: source and destination types are of the same length. + unsafe { + let v: &u32 = transmute(v); + log_bin32(self.recorder.ptr, self.slot.as_mut_ptr(), v as *const _); + } + }, + _ => return Err(Error), + } + Ok(()) + } + + #[inline] + fn write_i64(&mut self, v: &i64, spec: &FormatSpec) -> FmtResult { + // SAFETY: FFI calls. Reference is reinterpreted as a pointer of the same type. + match spec.get_display_hint() { + DisplayHint::NoHint => unsafe { log_i64(self.recorder.ptr, self.slot.as_mut_ptr(), v as *const _) }, + DisplayHint::LowerHex | DisplayHint::UpperHex => { + // SAFETY: source and destination types are of the same length. + unsafe { + let v: &u64 = transmute(v); + log_hex64(self.recorder.ptr, self.slot.as_mut_ptr(), v as *const _); + } + }, + DisplayHint::Binary => { + // SAFETY: source and destination types are of the same length. + unsafe { + let v: &u64 = transmute(v); + log_bin64(self.recorder.ptr, self.slot.as_mut_ptr(), v as *const _); + } + }, + _ => return Err(Error), + } + Ok(()) + } + + #[inline] + fn write_u8(&mut self, v: &u8, spec: &FormatSpec) -> FmtResult { + // SAFETY: FFI calls. Reference is reinterpreted as a pointer of the same type. + match spec.get_display_hint() { + DisplayHint::NoHint => unsafe { log_u8(self.recorder.ptr, self.slot.as_mut_ptr(), v as *const _) }, + DisplayHint::LowerHex | DisplayHint::UpperHex => unsafe { + log_hex8(self.recorder.ptr, self.slot.as_mut_ptr(), v as *const _) + }, + DisplayHint::Binary => unsafe { log_bin8(self.recorder.ptr, self.slot.as_mut_ptr(), v as *const _) }, + _ => return Err(Error), + } + Ok(()) + } + + #[inline] + fn write_u16(&mut self, v: &u16, spec: &FormatSpec) -> FmtResult { + // SAFETY: FFI calls. Reference is reinterpreted as a pointer of the same type. + match spec.get_display_hint() { + DisplayHint::NoHint => unsafe { log_u16(self.recorder.ptr, self.slot.as_mut_ptr(), v as *const _) }, + DisplayHint::LowerHex | DisplayHint::UpperHex => unsafe { + log_hex16(self.recorder.ptr, self.slot.as_mut_ptr(), v as *const _) + }, + DisplayHint::Binary => unsafe { log_bin16(self.recorder.ptr, self.slot.as_mut_ptr(), v as *const _) }, + _ => return Err(Error), + } + Ok(()) + } + + #[inline] + fn write_u32(&mut self, v: &u32, spec: &FormatSpec) -> FmtResult { + // SAFETY: FFI calls. Reference is reinterpreted as a pointer of the same type. + match spec.get_display_hint() { + DisplayHint::NoHint => unsafe { log_u32(self.recorder.ptr, self.slot.as_mut_ptr(), v as *const _) }, + DisplayHint::LowerHex | DisplayHint::UpperHex => unsafe { + log_hex32(self.recorder.ptr, self.slot.as_mut_ptr(), v as *const _) + }, + DisplayHint::Binary => unsafe { log_bin32(self.recorder.ptr, self.slot.as_mut_ptr(), v as *const _) }, + _ => return Err(Error), + } + Ok(()) + } + + #[inline] + fn write_u64(&mut self, v: &u64, spec: &FormatSpec) -> FmtResult { + // SAFETY: FFI calls. Reference is reinterpreted as a pointer of the same type. + match spec.get_display_hint() { + DisplayHint::NoHint => unsafe { log_u64(self.recorder.ptr, self.slot.as_mut_ptr(), v as *const _) }, + DisplayHint::LowerHex | DisplayHint::UpperHex => unsafe { + log_hex64(self.recorder.ptr, self.slot.as_mut_ptr(), v as *const _) + }, + DisplayHint::Binary => unsafe { log_bin64(self.recorder.ptr, self.slot.as_mut_ptr(), v as *const _) }, + _ => return Err(Error), + } + Ok(()) + } + + #[inline] + fn write_str(&mut self, v: &str, _spec: &FormatSpec) -> FmtResult { + // Get string as pointer and size. + let v_ptr = v.as_ptr().cast(); + let v_size = v.len(); + + // SAFETY: FFI call. String is reinterpreted into `c_char` pointer and number of bytes. + unsafe { + log_string(self.recorder.ptr, self.slot.as_mut_ptr(), v_ptr, v_size); + } + Ok(()) + } +} + +impl Drop for LogMessage<'_> { + fn drop(&mut self) { + // SAFETY: FFI call. Validity of objects is checked elsewhere. + unsafe { + recorder_stop(self.recorder.ptr, self.slot.as_mut_ptr()); + } + } +} + +unsafe extern "C" { + fn recorder_get() -> *mut RecorderPtr; + fn recorder_start( + recorder: *mut RecorderPtr, + context: *const c_char, + context_size: usize, + log_level: LogLevel, + slot: *mut SlotHandleStorage, + ) -> *mut SlotHandleStorage; + fn recorder_stop(recorder: *mut RecorderPtr, slot: *mut SlotHandleStorage); + fn recorder_log_level(recorder: *const RecorderPtr, context: *const c_char, context_size: usize) -> LogLevel; + fn log_bool(recorder: *mut RecorderPtr, slot: *mut SlotHandleStorage, value: *const bool); + fn log_f32(recorder: *mut RecorderPtr, slot: *mut SlotHandleStorage, value: *const f32); + fn log_f64(recorder: *mut RecorderPtr, slot: *mut SlotHandleStorage, value: *const f64); + fn log_string(recorder: *mut RecorderPtr, slot: *mut SlotHandleStorage, value: *const c_char, size: usize); + fn log_i8(recorder: *mut RecorderPtr, slot: *mut SlotHandleStorage, value: *const i8); + fn log_i16(recorder: *mut RecorderPtr, slot: *mut SlotHandleStorage, value: *const i16); + fn log_i32(recorder: *mut RecorderPtr, slot: *mut SlotHandleStorage, value: *const i32); + fn log_i64(recorder: *mut RecorderPtr, slot: *mut SlotHandleStorage, value: *const i64); + fn log_u8(recorder: *mut RecorderPtr, slot: *mut SlotHandleStorage, value: *const u8); + fn log_u16(recorder: *mut RecorderPtr, slot: *mut SlotHandleStorage, value: *const u16); + fn log_u32(recorder: *mut RecorderPtr, slot: *mut SlotHandleStorage, value: *const u32); + fn log_u64(recorder: *mut RecorderPtr, slot: *mut SlotHandleStorage, value: *const u64); + fn log_bin8(recorder: *mut RecorderPtr, slot: *mut SlotHandleStorage, value: *const u8); + fn log_bin16(recorder: *mut RecorderPtr, slot: *mut SlotHandleStorage, value: *const u16); + fn log_bin32(recorder: *mut RecorderPtr, slot: *mut SlotHandleStorage, value: *const u32); + fn log_bin64(recorder: *mut RecorderPtr, slot: *mut SlotHandleStorage, value: *const u64); + fn log_hex8(recorder: *mut RecorderPtr, slot: *mut SlotHandleStorage, value: *const u8); + fn log_hex16(recorder: *mut RecorderPtr, slot: *mut SlotHandleStorage, value: *const u16); + fn log_hex32(recorder: *mut RecorderPtr, slot: *mut SlotHandleStorage, value: *const u32); + fn log_hex64(recorder: *mut RecorderPtr, slot: *mut SlotHandleStorage, value: *const u64); + fn slot_handle_size() -> usize; + fn slot_handle_alignment() -> usize; +} + +#[cfg(test)] +mod tests { + use crate::ffi::{Context, LogLevel}; + + #[test] + fn test_log_level_from_score_log_level() { + let levels = [ + (score_log::Level::Fatal, LogLevel::Fatal), + (score_log::Level::Error, LogLevel::Error), + (score_log::Level::Warn, LogLevel::Warn), + (score_log::Level::Info, LogLevel::Info), + (score_log::Level::Debug, LogLevel::Debug), + (score_log::Level::Trace, LogLevel::Verbose), + ]; + + for (score_log_level, ffi_level) in levels.into_iter() { + assert_eq!(LogLevel::from(score_log_level), ffi_level); + } + } + + #[test] + fn test_score_log_level_from_log_level() { + let levels = [ + (score_log::Level::Fatal, LogLevel::Fatal), + (score_log::Level::Error, LogLevel::Error), + (score_log::Level::Warn, LogLevel::Warn), + (score_log::Level::Info, LogLevel::Info), + (score_log::Level::Debug, LogLevel::Debug), + (score_log::Level::Trace, LogLevel::Verbose), + ]; + + for (score_log_level, ffi_level) in levels.into_iter() { + assert_eq!(score_log::Level::from(ffi_level), score_log_level); + } + } + + #[test] + fn test_score_log_level_filter_from_log_level() { + let levels = [ + (score_log::LevelFilter::Off, LogLevel::Off), + (score_log::LevelFilter::Fatal, LogLevel::Fatal), + (score_log::LevelFilter::Error, LogLevel::Error), + (score_log::LevelFilter::Warn, LogLevel::Warn), + (score_log::LevelFilter::Info, LogLevel::Info), + (score_log::LevelFilter::Debug, LogLevel::Debug), + (score_log::LevelFilter::Trace, LogLevel::Verbose), + ]; + + for (score_log_level_filter, ffi_level) in levels.into_iter() { + assert_eq!(score_log::LevelFilter::from(ffi_level), score_log_level_filter); + } + } + + #[test] + fn test_context_single_char() { + let context = Context::from("X"); + assert_eq!(context.size, 1); + assert_eq!(context.data, [88, 0, 0, 0]); + } + + #[test] + fn test_context_four_chars() { + let context = Context::from("TEST"); + assert_eq!(context.size, 4); + assert_eq!(context.data, [84, 69, 83, 84]); + } + + #[test] + fn test_context_trimmed() { + let context = Context::from("trimmed"); + assert_eq!(context.size, 4); + assert_eq!(context.data, [116, 114, 105, 109]); + } + + #[test] + #[should_panic(expected = "provided context contains non-ASCII characters: ")] + fn test_context_non_ascii() { + let _ = Context::from("❌"); + } +} diff --git a/score/mw/log/rust/score_log_bridge/src/lib.rs b/score/mw/log/rust/score_log_bridge/src/lib.rs new file mode 100644 index 0000000..0dfd5cc --- /dev/null +++ b/score/mw/log/rust/score_log_bridge/src/lib.rs @@ -0,0 +1,23 @@ +// +// 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 +// +// +// SPDX-License-Identifier: Apache-2.0 +// + +//! C++-based backend for `score_log`. + +#![warn(missing_docs)] +#![warn(clippy::std_instead_of_core)] +#![warn(clippy::alloc_instead_of_core)] + +mod ffi; +mod score_logger; + +pub use crate::score_logger::{ScoreLogger, ScoreLoggerBuilder}; diff --git a/score/mw/log/rust/score_log_bridge/src/score_logger.rs b/score/mw/log/rust/score_log_bridge/src/score_logger.rs new file mode 100644 index 0000000..9b4e3af --- /dev/null +++ b/score/mw/log/rust/score_log_bridge/src/score_logger.rs @@ -0,0 +1,346 @@ +// +// 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 +// +// +// SPDX-License-Identifier: Apache-2.0 +// + +//! C++-based logger implementation + +use crate::ffi::{Context, LogLevel, LogMessage, Recorder, SlotHandleStorage}; +use score_log::fmt::{score_write, write}; +use score_log::{Log, Metadata, Record}; +use std::env::{set_var, var_os}; +use std::path::PathBuf; + +/// Builder for the [`ScoreLogger`]. +pub struct ScoreLoggerBuilder { + context: Context, + show_module: bool, + show_file: bool, + show_line: bool, + config_path: Option, +} + +impl ScoreLoggerBuilder { + /// Create builder with default parameters. + /// + /// # Panics + /// + /// Data layout check is performed. + /// This might cause panic if layout of FFI structures is mismatched. + pub fn new() -> Self { + Self::default() + } + + /// Set context for the [`ScoreLogger`]. + /// + /// Only ASCII characters are allowed. + /// Max 4 characters are used. Rest of the provided string will be trimmed. + pub fn context(mut self, context: &str) -> Self { + self.context = Context::from(context); + self + } + + /// Show module name in logs. + pub fn show_module(mut self, show_module: bool) -> Self { + self.show_module = show_module; + self + } + + /// Show file name in logs. + pub fn show_file(mut self, show_file: bool) -> Self { + self.show_file = show_file; + self + } + + /// Show line number in logs. + pub fn show_line(mut self, show_line: bool) -> Self { + self.show_line = show_line; + self + } + + /// Set path to the logging configuration. + /// `MW_LOG_CONFIG_FILE` environment variable is set during [`Self::set_as_default_logger`]. + /// + /// Following conditions must be met: + /// - Variable is set only during the first call to [`Self::set_as_default_logger`]. + /// - Variable is set only if not set externally. + pub fn config(mut self, config_path: PathBuf) -> Self { + self.config_path = Some(config_path); + self + } + + /// Build the [`ScoreLogger`] with provided context and configuration. + pub fn build(self) -> ScoreLogger { + let recorder = Recorder::new(); + ScoreLogger { + context: self.context, + show_module: self.show_module, + show_file: self.show_file, + show_line: self.show_line, + recorder, + } + } + + /// Build the [`ScoreLogger`] and set it as the default logger. + /// + /// # Safety + /// + /// Method sets `MW_LOG_CONFIG_FILE` environment variable. + /// Setting it is safe only before any other thread started. + pub fn set_as_default_logger(self) { + // Set `MW_LOG_CONFIG_FILE`. + { + const KEY: &str = "MW_LOG_CONFIG_FILE"; + + // Set variable only if: + // - environment variable is not set + // - `config_path` is set and not empty + if var_os(KEY).is_none() { + if let Some(ref path) = self.config_path { + let path_os_str = path.as_os_str(); + if !path_os_str.is_empty() { + // SAFETY: + // Safe only before any other thread started. + // Operation is performed only once. + unsafe { set_var(KEY, path_os_str) }; + } + } + } + } + + // Build logger and set as default. + let context = self.context.clone(); + let logger = self.build(); + score_log::set_max_level(logger.log_level(&context).into()); + if let Err(e) = score_log::set_global_logger(Box::new(logger)) { + panic!("unable to set logger: {e}"); + } + } +} + +impl Default for ScoreLoggerBuilder { + /// Create builder with default parameters. + /// + /// # Panics + /// + /// Data layout check is performed. + /// This might cause panic if layout of FFI structures is mismatched. + fn default() -> Self { + // Perform layout check. + let slot_layout_rust = SlotHandleStorage::layout_rust(); + let slot_layout_cpp = SlotHandleStorage::layout_cpp(); + assert!( + slot_layout_rust == slot_layout_cpp, + "SlotHandle layout mismatch, this indicates compilation settings misalignment (Rust: {slot_layout_rust:?}, C++: {slot_layout_cpp:?})" + ); + + // Create builder with default parameters. + Self { + context: Context::from("DFLT"), + show_module: false, + show_file: false, + show_line: false, + config_path: None, + } + } +} + +/// C++-based logger implementation. +pub struct ScoreLogger { + context: Context, + show_module: bool, + show_file: bool, + show_line: bool, + recorder: Recorder, +} + +impl ScoreLogger { + /// Current log level for provided context. + pub(crate) fn log_level(&self, context: &Context) -> LogLevel { + self.recorder.log_level(context) + } +} + +impl Log for ScoreLogger { + fn enabled(&self, metadata: &Metadata) -> bool { + let context = Context::from(metadata.context()); + self.log_level(&context) >= metadata.level().into() + } + + fn context(&self) -> &str { + <&str>::from(&self.context) + } + + fn log(&self, record: &Record) { + // Finish early if not enabled for requested level. + let metadata = record.metadata(); + if !self.enabled(metadata) { + return; + } + + // Create log message. + let context = Context::from(metadata.context()); + let log_level = metadata.level().into(); + // Finish early if unable to create message. + let mut log_message = match LogMessage::new(&self.recorder, &context, log_level) { + Ok(log_message) => log_message, + Err(_) => return, + }; + + // Write module, file and line. + if self.show_module || self.show_file || self.show_line { + let _ = score_write!(&mut log_message, "["); + if self.show_module { + let _ = score_write!(&mut log_message, "{}:", record.module_path()); + } + if self.show_file { + let _ = score_write!(&mut log_message, "{}:", record.file()); + } + if self.show_line { + let _ = score_write!(&mut log_message, "{}", record.line()); + } + let _ = score_write!(&mut log_message, "]"); + } + + // Write log data. + let _ = write(&mut log_message, *record.args()); + // Written data is flushed on log message drop. + } + + fn flush(&self) { + // No-op. + } +} + +#[cfg(test)] +mod tests { + use crate::ScoreLoggerBuilder; + use std::env::var_os; + use std::path::PathBuf; + + #[test] + fn test_builder_new() { + let builder = ScoreLoggerBuilder::new(); + assert_eq!(builder.context, "DFLT".into()); + assert!(!builder.show_module); + assert!(!builder.show_file); + assert!(!builder.show_line); + assert_eq!(builder.config_path, None); + } + + #[test] + fn test_builder_default() { + let builder = ScoreLoggerBuilder::default(); + assert_eq!(builder.context, "DFLT".into()); + assert!(!builder.show_module); + assert!(!builder.show_file); + assert!(!builder.show_line); + assert_eq!(builder.config_path, None); + } + + #[test] + fn test_builder_context() { + let builder = ScoreLoggerBuilder::new().context("NEW_CONTEXT"); + assert_eq!(builder.context, "NEW_CONTEXT".into()); + assert!(!builder.show_module); + assert!(!builder.show_file); + assert!(!builder.show_line); + assert_eq!(builder.config_path, None); + } + + #[test] + fn test_builder_show_module() { + let builder = ScoreLoggerBuilder::new().show_module(true); + assert_eq!(builder.context, "DFLT".into()); + assert!(builder.show_module); + assert!(!builder.show_file); + assert!(!builder.show_line); + assert_eq!(builder.config_path, None); + } + + #[test] + fn test_builder_show_file() { + let builder = ScoreLoggerBuilder::new().show_file(true); + assert_eq!(builder.context, "DFLT".into()); + assert!(!builder.show_module); + assert!(builder.show_file); + assert!(!builder.show_line); + assert_eq!(builder.config_path, None); + } + + #[test] + fn test_builder_show_line() { + let builder = ScoreLoggerBuilder::new().show_line(true); + assert_eq!(builder.context, "DFLT".into()); + assert!(!builder.show_module); + assert!(!builder.show_file); + assert!(builder.show_line); + assert_eq!(builder.config_path, None); + } + + #[test] + fn test_builder_config_path() { + let builder = ScoreLoggerBuilder::new().config(PathBuf::from("/some/path")); + assert_eq!(builder.context, "DFLT".into()); + assert!(!builder.show_module); + assert!(!builder.show_file); + assert!(!builder.show_line); + assert_eq!(builder.config_path, Some(PathBuf::from("/some/path"))); + } + + #[test] + fn test_builder_chained() { + let builder = ScoreLoggerBuilder::new() + .context("NEW_CONTEXT") + .show_module(true) + .show_file(true) + .show_line(true) + .config(PathBuf::from("/some/path")); + assert_eq!(builder.context, "NEW_CONTEXT".into()); + assert!(builder.show_module); + assert!(builder.show_file); + assert!(builder.show_line); + assert_eq!(builder.config_path, Some(PathBuf::from("/some/path"))); + } + + #[test] + fn test_builder_build() { + let logger = ScoreLoggerBuilder::new() + .context("NEW_CONTEXT") + .show_module(true) + .show_file(true) + .show_line(true) + .build(); + assert_eq!(logger.context, "NEW_CONTEXT".into()); + assert!(logger.show_module); + assert!(logger.show_file); + assert!(logger.show_line); + } + + #[test] + fn test_builder_set_as_default_logger() { + // Pre-check `MW_LOG_CONFIG_FILE` is not set. + const KEY: &str = "MW_LOG_CONFIG_FILE"; + assert!(var_os(KEY).is_none()); + + let config_path = PathBuf::from("/some/path"); + ScoreLoggerBuilder::new() + .context("NEW_CONTEXT") + .show_module(true) + .show_file(true) + .show_line(true) + .config(config_path.clone()) + .set_as_default_logger(); + + // Check environment variable. + assert!(var_os(KEY).is_some_and(|p| p == config_path)); + } +} From 840beab7f258e7d7326407592af957398c6c13ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20J=C4=99drzejewski?= <32277297+arkjedrz@users.noreply.github.com> Date: Fri, 23 Jan 2026 15:26:19 +0100 Subject: [PATCH 23/34] bazel: fix `"cc_*" is not global anymore` (#24) Load required `cc_*` functions. --- score/datarouter/BUILD | 1 + score/datarouter/daemon_communication/BUILD | 1 + score/datarouter/dlt_filetransfer_trigger_lib/BUILD | 1 + .../dynamic_configuration/config_session_factory/BUILD | 1 + .../datarouter/dynamic_configuration/config_session_stub/BUILD | 1 + score/datarouter/dynamic_configuration/i_session/BUILD | 1 + score/datarouter/error/BUILD | 1 + score/datarouter/file_transfer/BUILD | 1 + score/datarouter/file_transfer/file_transfer_stub/BUILD | 1 + score/datarouter/lib/synchronized/BUILD | 2 ++ score/datarouter/network/BUILD | 2 ++ score/datarouter/nonverbose_dlt_stub/BUILD | 1 + score/datarouter/persistent_logging/BUILD | 2 ++ .../persistent_logging/persistent_logging_stub/BUILD | 2 ++ score/datarouter/src/persistency/BUILD | 1 + .../src/persistency/stub_persistent_dictionary/BUILD | 1 + score/datarouter/test/ut/ut_logging/BUILD | 1 + score/datarouter/test/utils/BUILD | 2 ++ score/mw/log/BUILD | 2 ++ score/mw/log/custom_recorder_example/BUILD | 2 ++ score/mw/log/detail/common/BUILD | 2 ++ score/mw/log/detail/data_router/BUILD | 1 + score/mw/log/detail/data_router/shared_memory/BUILD | 1 + score/mw/log/detail/file_recorder/BUILD | 1 + score/mw/log/detail/slog/BUILD | 1 + score/mw/log/detail/wait_free_producer_queue/BUILD | 1 + score/mw/log/flags/BUILD | 1 + score/mw/log/legacy_non_verbose_api/BUILD | 1 + score/mw/log/test/console_logging_environment/BUILD | 2 ++ tests/cpp/BUILD | 3 +++ third_party/traceability/tools/source_code_linker/BUILD | 2 ++ 31 files changed, 43 insertions(+) diff --git a/score/datarouter/BUILD b/score/datarouter/BUILD index 14510e9..77f4fdc 100644 --- a/score/datarouter/BUILD +++ b/score/datarouter/BUILD @@ -11,6 +11,7 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library") load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") ## =========================================================================== diff --git a/score/datarouter/daemon_communication/BUILD b/score/datarouter/daemon_communication/BUILD index b67b2a6..136356e 100644 --- a/score/datarouter/daemon_communication/BUILD +++ b/score/datarouter/daemon_communication/BUILD @@ -11,6 +11,7 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +load("@rules_cc//cc:defs.bzl", "cc_library") load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") cc_library( diff --git a/score/datarouter/dlt_filetransfer_trigger_lib/BUILD b/score/datarouter/dlt_filetransfer_trigger_lib/BUILD index b4c4e69..d910cbd 100644 --- a/score/datarouter/dlt_filetransfer_trigger_lib/BUILD +++ b/score/datarouter/dlt_filetransfer_trigger_lib/BUILD @@ -11,6 +11,7 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +load("@rules_cc//cc:defs.bzl", "cc_library") load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") ## =========================================================================== diff --git a/score/datarouter/dynamic_configuration/config_session_factory/BUILD b/score/datarouter/dynamic_configuration/config_session_factory/BUILD index 67cd42b..c94f857 100644 --- a/score/datarouter/dynamic_configuration/config_session_factory/BUILD +++ b/score/datarouter/dynamic_configuration/config_session_factory/BUILD @@ -11,6 +11,7 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +load("@rules_cc//cc:defs.bzl", "cc_library") load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") [ diff --git a/score/datarouter/dynamic_configuration/config_session_stub/BUILD b/score/datarouter/dynamic_configuration/config_session_stub/BUILD index a932af6..fd8e2a5 100644 --- a/score/datarouter/dynamic_configuration/config_session_stub/BUILD +++ b/score/datarouter/dynamic_configuration/config_session_stub/BUILD @@ -11,6 +11,7 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +load("@rules_cc//cc:defs.bzl", "cc_library") load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") [ diff --git a/score/datarouter/dynamic_configuration/i_session/BUILD b/score/datarouter/dynamic_configuration/i_session/BUILD index dce7108..7cdb71f 100644 --- a/score/datarouter/dynamic_configuration/i_session/BUILD +++ b/score/datarouter/dynamic_configuration/i_session/BUILD @@ -11,6 +11,7 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +load("@rules_cc//cc:defs.bzl", "cc_library") load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") cc_library( diff --git a/score/datarouter/error/BUILD b/score/datarouter/error/BUILD index 8461f01..237e02d 100644 --- a/score/datarouter/error/BUILD +++ b/score/datarouter/error/BUILD @@ -11,6 +11,7 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +load("@rules_cc//cc:defs.bzl", "cc_library") load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") cc_library( diff --git a/score/datarouter/file_transfer/BUILD b/score/datarouter/file_transfer/BUILD index c849b2a..867f662 100644 --- a/score/datarouter/file_transfer/BUILD +++ b/score/datarouter/file_transfer/BUILD @@ -11,6 +11,7 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +load("@rules_cc//cc:defs.bzl", "cc_library") load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") cc_library( diff --git a/score/datarouter/file_transfer/file_transfer_stub/BUILD b/score/datarouter/file_transfer/file_transfer_stub/BUILD index c9de443..225dd3d 100644 --- a/score/datarouter/file_transfer/file_transfer_stub/BUILD +++ b/score/datarouter/file_transfer/file_transfer_stub/BUILD @@ -11,6 +11,7 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +load("@rules_cc//cc:defs.bzl", "cc_library") load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") cc_library( diff --git a/score/datarouter/lib/synchronized/BUILD b/score/datarouter/lib/synchronized/BUILD index aa18eb7..a9b787f 100644 --- a/score/datarouter/lib/synchronized/BUILD +++ b/score/datarouter/lib/synchronized/BUILD @@ -11,6 +11,8 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") + # Synchronized utility BUILD file FEAT_COMPILER_WARNINGS_AS_ERRORS = [ diff --git a/score/datarouter/network/BUILD b/score/datarouter/network/BUILD index aaf4643..9aee0a2 100644 --- a/score/datarouter/network/BUILD +++ b/score/datarouter/network/BUILD @@ -11,6 +11,8 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +load("@rules_cc//cc:defs.bzl", "cc_library") + cc_library( name = "vlan", hdrs = [ diff --git a/score/datarouter/nonverbose_dlt_stub/BUILD b/score/datarouter/nonverbose_dlt_stub/BUILD index f33ee30..e4e672c 100644 --- a/score/datarouter/nonverbose_dlt_stub/BUILD +++ b/score/datarouter/nonverbose_dlt_stub/BUILD @@ -11,6 +11,7 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +load("@rules_cc//cc:defs.bzl", "cc_library") load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") [ diff --git a/score/datarouter/persistent_logging/BUILD b/score/datarouter/persistent_logging/BUILD index b888ac5..194f120 100644 --- a/score/datarouter/persistent_logging/BUILD +++ b/score/datarouter/persistent_logging/BUILD @@ -11,6 +11,8 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +load("@rules_cc//cc:defs.bzl", "cc_library") + cc_library( name = "sysedr_handler_interface", hdrs = [ diff --git a/score/datarouter/persistent_logging/persistent_logging_stub/BUILD b/score/datarouter/persistent_logging/persistent_logging_stub/BUILD index 2bd4aaf..3f9fb32 100644 --- a/score/datarouter/persistent_logging/persistent_logging_stub/BUILD +++ b/score/datarouter/persistent_logging/persistent_logging_stub/BUILD @@ -11,6 +11,8 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +load("@rules_cc//cc:defs.bzl", "cc_library") + cc_library( name = "sysedr_stub", srcs = [ diff --git a/score/datarouter/src/persistency/BUILD b/score/datarouter/src/persistency/BUILD index 0574786..9df2222 100644 --- a/score/datarouter/src/persistency/BUILD +++ b/score/datarouter/src/persistency/BUILD @@ -11,6 +11,7 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +load("@rules_cc//cc:defs.bzl", "cc_library") load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") cc_library( diff --git a/score/datarouter/src/persistency/stub_persistent_dictionary/BUILD b/score/datarouter/src/persistency/stub_persistent_dictionary/BUILD index 478c19c..93e0d74 100644 --- a/score/datarouter/src/persistency/stub_persistent_dictionary/BUILD +++ b/score/datarouter/src/persistency/stub_persistent_dictionary/BUILD @@ -11,6 +11,7 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +load("@rules_cc//cc:defs.bzl", "cc_library") load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") cc_library( diff --git a/score/datarouter/test/ut/ut_logging/BUILD b/score/datarouter/test/ut/ut_logging/BUILD index 31d02bb..3979213 100644 --- a/score/datarouter/test/ut/ut_logging/BUILD +++ b/score/datarouter/test/ut/ut_logging/BUILD @@ -11,6 +11,7 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +load("@rules_cc//cc:defs.bzl", "cc_test") load("@score_baselibs//third_party/itf:py_unittest_qnx_test.bzl", "py_unittest_qnx_test") FEAT_COMPILER_WARNINGS_AS_ERRORS = [ diff --git a/score/datarouter/test/utils/BUILD b/score/datarouter/test/utils/BUILD index f98e189..090d529 100644 --- a/score/datarouter/test/utils/BUILD +++ b/score/datarouter/test/utils/BUILD @@ -11,6 +11,8 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +load("@rules_cc//cc:defs.bzl", "cc_library") + cc_library( name = "utils", testonly = True, diff --git a/score/mw/log/BUILD b/score/mw/log/BUILD index 7eb3436..a86b2fd 100644 --- a/score/mw/log/BUILD +++ b/score/mw/log/BUILD @@ -11,6 +11,8 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +load("@rules_cc//cc:defs.bzl", "cc_library") + cc_library( name = "log", visibility = ["//visibility:public"], diff --git a/score/mw/log/custom_recorder_example/BUILD b/score/mw/log/custom_recorder_example/BUILD index 92a1369..3cf90f0 100644 --- a/score/mw/log/custom_recorder_example/BUILD +++ b/score/mw/log/custom_recorder_example/BUILD @@ -11,6 +11,8 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library") + cc_library( name = "custom_recorder_example", srcs = [ diff --git a/score/mw/log/detail/common/BUILD b/score/mw/log/detail/common/BUILD index 379dd43..2262c5b 100644 --- a/score/mw/log/detail/common/BUILD +++ b/score/mw/log/detail/common/BUILD @@ -10,6 +10,8 @@ # # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* + +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load("@score_baselibs//:bazel/unit_tests.bzl", "cc_unit_test_suites_for_host_and_qnx") COMPILER_WARNING_FEATURES = [ diff --git a/score/mw/log/detail/data_router/BUILD b/score/mw/log/detail/data_router/BUILD index 2b457df..9ebad9a 100644 --- a/score/mw/log/detail/data_router/BUILD +++ b/score/mw/log/detail/data_router/BUILD @@ -11,6 +11,7 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load("@score_baselibs//:bazel/unit_tests.bzl", "cc_unit_test_suites_for_host_and_qnx") load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") diff --git a/score/mw/log/detail/data_router/shared_memory/BUILD b/score/mw/log/detail/data_router/shared_memory/BUILD index 6179879..0bdbc05 100644 --- a/score/mw/log/detail/data_router/shared_memory/BUILD +++ b/score/mw/log/detail/data_router/shared_memory/BUILD @@ -11,6 +11,7 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load("@score_baselibs//:bazel/unit_tests.bzl", "cc_unit_test_suites_for_host_and_qnx") cc_library( diff --git a/score/mw/log/detail/file_recorder/BUILD b/score/mw/log/detail/file_recorder/BUILD index 00185d5..91f6c15 100644 --- a/score/mw/log/detail/file_recorder/BUILD +++ b/score/mw/log/detail/file_recorder/BUILD @@ -11,6 +11,7 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load("@score_baselibs//:bazel/unit_tests.bzl", "cc_unit_test_suites_for_host_and_qnx") COMPILER_WARNING_FEATURES = [ diff --git a/score/mw/log/detail/slog/BUILD b/score/mw/log/detail/slog/BUILD index 7b11516..5db838c 100644 --- a/score/mw/log/detail/slog/BUILD +++ b/score/mw/log/detail/slog/BUILD @@ -11,6 +11,7 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load("@score_baselibs//third_party/itf:py_unittest_qnx_test.bzl", "py_unittest_qnx_test") load("//:score/mw/common_features.bzl", "COMPILER_WARNING_FEATURES") diff --git a/score/mw/log/detail/wait_free_producer_queue/BUILD b/score/mw/log/detail/wait_free_producer_queue/BUILD index 730a712..cc03fb4 100644 --- a/score/mw/log/detail/wait_free_producer_queue/BUILD +++ b/score/mw/log/detail/wait_free_producer_queue/BUILD @@ -11,6 +11,7 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load("@score_baselibs//:bazel/unit_tests.bzl", "cc_unit_test_suites_for_host_and_qnx") load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") diff --git a/score/mw/log/flags/BUILD b/score/mw/log/flags/BUILD index 9a4bd59..0adc312 100644 --- a/score/mw/log/flags/BUILD +++ b/score/mw/log/flags/BUILD @@ -12,6 +12,7 @@ # ******************************************************************************* load("@bazel_skylib//rules:common_settings.bzl", "bool_flag") +load("@rules_cc//cc:defs.bzl", "cc_library") bool_flag( name = "KRemote_Logging", diff --git a/score/mw/log/legacy_non_verbose_api/BUILD b/score/mw/log/legacy_non_verbose_api/BUILD index 55546f1..6b07f5c 100644 --- a/score/mw/log/legacy_non_verbose_api/BUILD +++ b/score/mw/log/legacy_non_verbose_api/BUILD @@ -11,6 +11,7 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load("@score_baselibs//:bazel/unit_tests.bzl", "cc_unit_test_suites_for_host_and_qnx") load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES") diff --git a/score/mw/log/test/console_logging_environment/BUILD b/score/mw/log/test/console_logging_environment/BUILD index 8b5a122..64cf16b 100644 --- a/score/mw/log/test/console_logging_environment/BUILD +++ b/score/mw/log/test/console_logging_environment/BUILD @@ -11,6 +11,8 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +load("@rules_cc//cc:defs.bzl", "cc_library") + # You can depend on this if you don't need further customization of the main() for gtest. cc_library( name = "console_logging_environment", diff --git a/tests/cpp/BUILD b/tests/cpp/BUILD index e3b354e..1d7b2a3 100644 --- a/tests/cpp/BUILD +++ b/tests/cpp/BUILD @@ -10,6 +10,9 @@ # # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* + +load("@rules_cc//cc:defs.bzl", "cc_test") + cc_test( name = "cpp_test_main", srcs = ["test_main.cpp"], diff --git a/third_party/traceability/tools/source_code_linker/BUILD b/third_party/traceability/tools/source_code_linker/BUILD index 5806e9b..1e0cd98 100644 --- a/third_party/traceability/tools/source_code_linker/BUILD +++ b/third_party/traceability/tools/source_code_linker/BUILD @@ -11,6 +11,8 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +load("@rules_python//python:defs.bzl", "py_binary") + # Carry over from Eclipse S-Core including slight modifications: # https://github.com/eclipse-score/docs-as-code/tree/v0.4.0/src/extensions/score_source_code_linker From 934cbe70f50e5fca84a04ebddedda8b728db6585 Mon Sep 17 00:00:00 2001 From: Raghavendra Maddikery Date: Fri, 23 Jan 2026 16:53:07 +0100 Subject: [PATCH 24/34] Make trlc a dev_dependency (#39) - So that it does not propagate to other users of this module. --- MODULE.bazel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MODULE.bazel b/MODULE.bazel index b359b9a..dfb0154 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -74,7 +74,7 @@ gcc.toolchain( ) # TRLC dependency for requirements traceability -bazel_dep(name = "trlc", version = "0.0.0") +bazel_dep(name = "trlc", version = "0.0.0", dev_dependency = True) git_override( module_name = "trlc", commit = "650b51a47264a4f232b3341f473527710fc32669", # trlc-2.0.2 release From a5aceb5baf8c2d7e86025861299a418e92b51f72 Mon Sep 17 00:00:00 2001 From: Pawel Rutka Date: Mon, 26 Jan 2026 09:34:02 +0100 Subject: [PATCH 25/34] Code owners must be commiters (#36) --- .github/CODEOWNERS | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 2d4c33d..66bb8c9 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,13 +1,13 @@ # default owners -* @antonkri @pawelrutkaq @arkjedrz @arsibo +* @antonkri @pawelrutkaq @arsibo # datarouter owners -/score/datarouter @rmaddikery @hoppe-and-dreams @mobileinfo @andreapefe @RSingh1511 @Y-Vaishnavi +/score/datarouter @antonkri # mw/log owners -/score/mw/common_features.bzl @rmaddikery @hoppe-and-dreams @mobileinfo @andreapefe @RSingh1511 @Y-Vaishnavi +/score/mw/common_features.bzl @antonkri -/score/mw/log @rmaddikery @hoppe-and-dreams @mobileinfo @andreapefe @RSingh1511 @Y-Vaishnavi +/score/mw/log @antonkri # mw/log rust mw_logger owners (more specific rule overrides general rule above) -/score/mw/log/rust/mw_logger @pawelrutkaq @arkjedrz +/score/mw/log/rust/mw_logger @pawelrutkaq From a8a92f8d35593e1acc0f1b22d2a0b4ff0bf06692 Mon Sep 17 00:00:00 2001 From: Pawel Rutka Date: Tue, 27 Jan 2026 09:25:32 +0100 Subject: [PATCH 26/34] Fix codeowners for Rust (#42) --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 66bb8c9..7037561 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -10,4 +10,4 @@ /score/mw/log @antonkri # mw/log rust mw_logger owners (more specific rule overrides general rule above) -/score/mw/log/rust/mw_logger @pawelrutkaq +/score/mw/log/rust @pawelrutkaq From f159201b49b2741a71a751415512caa1f3cbb5e9 Mon Sep 17 00:00:00 2001 From: lurtz <727209+lurtz@users.noreply.github.com> Date: Wed, 28 Jan 2026 12:52:59 +0100 Subject: [PATCH 27/34] chore: use devcontainer image v1.1.0 (#45) Always using the latest version might result in surprise build breaks. It should be updated with commits. --- .../alt_container_docker_on_windows/devcontainer.json | 5 ----- .devcontainer/devcontainer.json | 3 +-- 2 files changed, 1 insertion(+), 7 deletions(-) delete mode 100644 .devcontainer/alt_container_docker_on_windows/devcontainer.json diff --git a/.devcontainer/alt_container_docker_on_windows/devcontainer.json b/.devcontainer/alt_container_docker_on_windows/devcontainer.json deleted file mode 100644 index 452fe22..0000000 --- a/.devcontainer/alt_container_docker_on_windows/devcontainer.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "eclipse-s-core-docker-on-windows", - "image": "ghcr.io/eclipse-score/devcontainer:latest", - "initializeCommand": "IF not exist ${localEnv:HOME}\\.cache\\bazel ( mkdir ${localEnv:HOME}\\.cache\\bazel )" -} diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 3a0ec07..e88d473 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,5 +1,4 @@ { "name": "eclipse-s-core", - "image": "ghcr.io/eclipse-score/devcontainer:latest", - "initializeCommand": "mkdir -p ${localEnv:HOME}/.cache/bazel" + "image": "ghcr.io/eclipse-score/devcontainer:v1.1.0" } From eee2686ca39f08b89cf9c728af06269d281883ff Mon Sep 17 00:00:00 2001 From: Rahul Singh <55795456+RSingh1511@users.noreply.github.com> Date: Wed, 28 Jan 2026 18:05:33 +0530 Subject: [PATCH 28/34] QNX Toolchain setup for eclipse-score-logging (#27) * QNX Toolchain setup for eclipse-score-logging - Add qnx_x86_64 and qnx_arm64 build configurations in .bazelrc - Register QNX QCC toolchains for x86_64 and aarch64 in MODULE.bazel - Make qnx_creds.py executable for credential helper functionality Issue: SWP-235282 * QNX Toolchain setup for eclipse-score-logging - Add common:qnx base config to eliminate duplication. - Set qnx_creds.py as executable for credential helper Issue: SWP-235282 * QNX Toolchain setup for eclipse-score-logging - Add QNX build configs and CI workflow using reusable workflows * QNX Toolchain setup for eclipse-score-logging - Add copyright header to QNX CI workflow file * Add copyright header to QNX workflow file * Add QNX build support with reusable CI workflows Configure QNX toolchains for x86_64 and arm64 platforms with S-CORE reusable workflows. Enable Rust support for arm64-qnx. * Add QNX build support with Ferrocene Rust toolchains - Upgrade to score_toolchains_rust@0.4.0 for x86_64 QNX Rust support - Update workflow to build both x86_64 and arm64 QNX - Exclude Python targets from QNX builds * Scope QNX CI builds to production code following baselibs_rust pattern --- .bazelrc | 26 ++++++++++++++++++++++---- .github/workflows/build_qnx8.yml | 27 +++++++++++++++++++++------ MODULE.bazel | 19 ++----------------- scripts/internal/qnx_creds.py | 0 4 files changed, 45 insertions(+), 27 deletions(-) mode change 100644 => 100755 scripts/internal/qnx_creds.py diff --git a/.bazelrc b/.bazelrc index 7796054..0f4104e 100644 --- a/.bazelrc +++ b/.bazelrc @@ -34,16 +34,34 @@ build --experimental_retain_test_configuration_across_testonly #https://github.c common --registry=https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/ common --registry=https://bcr.bazel.build -common --credential_helper=*.qnx.com=%workspace%/scripts/internal/qnx_creds.py + +# Base QNX config (shared flags) +common:qnx --host_platform=@score_bazel_platforms//:x86_64-linux +common:qnx --credential_helper=*.qnx.com=%workspace%/scripts/internal/qnx_creds.py +common:qnx --sandbox_writable_path=/var/tmp build:build_qnx8 --platforms=@score_bazel_platforms//:arm64-qnx8_0 build:build_qnx8 --extra_toolchains=@toolchains_qnx_qcc//:qcc_aarch64 -build:build_qnx8 --extra_toolchains=@score_toolchains_rust//toolchains/aarch64-unknown-qnx8_0:toolchain_aarch64_qnx8_0 -build:build_qnx8 --extra_toolchains=@toolchains_qnx_ifs//:ifs_x86_64 build:build_qnx8 --extra_toolchains=@toolchains_qnx_ifs//:ifs_aarch64 +build:build_qnx8 --extra_toolchains=@score_toolchains_rust//toolchains/ferrocene:ferrocene_aarch64_unknown_nto_qnx800 + +common:qnx_x86_64 --config=qnx +common:qnx_x86_64 --platforms=@score_bazel_platforms//:x86_64-qnx8_0 +common:qnx_x86_64 --extra_toolchains=@toolchains_qnx_ifs//:ifs_x86_64 +common:qnx_x86_64 --extra_toolchains=@toolchains_qnx_qcc//:qcc_x86_64 +common:qnx_x86_64 --extra_toolchains=@score_toolchains_rust//toolchains/ferrocene:ferrocene_x86_64_pc_nto_qnx800 + +common:qnx_arm64 --config=qnx +common:qnx_arm64 --platforms=@score_bazel_platforms//:arm64-qnx8_0 +common:qnx_arm64 --extra_toolchains=@toolchains_qnx_ifs//:ifs_aarch64 +common:qnx_arm64 --extra_toolchains=@toolchains_qnx_qcc//:qcc_aarch64 +common:qnx_arm64 --extra_toolchains=@score_toolchains_rust//toolchains/ferrocene:ferrocene_aarch64_unknown_nto_qnx800 + +common:x86_64-qnx --config=qnx_x86_64 +common:arm64-qnx --config=qnx_arm64 common --extra_toolchains=@gcc_toolchain//:host_gcc_12 -common --extra_toolchains=@score_toolchains_rust//toolchains/x86_64-unknown-linux-gnu:toolchain_x86_64_linux +common --extra_toolchains=@score_toolchains_rust//toolchains/ferrocene:ferrocene_x86_64_unknown_linux_gnu # With this instrumentation filter for our two main components, we ensure that `bazel coverage //...` is yielding the correct results coverage --instrumentation_filter="^//score/datarouter[/:],^//score/mw/log[/:],-//score/mw/.*/test[/:]" diff --git a/.github/workflows/build_qnx8.yml b/.github/workflows/build_qnx8.yml index 7ec5c4c..23a024b 100644 --- a/.github/workflows/build_qnx8.yml +++ b/.github/workflows/build_qnx8.yml @@ -11,21 +11,36 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* -name: QNX8 Build +name: QNX8 Build and Test + on: pull_request_target: types: [opened, reopened, synchronize] - merge_group: - types: [checks_requested] + jobs: - qnx-build: + qnx-build-x86_64: + uses: eclipse-score/cicd-workflows/.github/workflows/qnx-build.yml@main + permissions: + contents: read + pull-requests: read + with: + bazel-target: "//score/... //tests/..." + bazel-config: "x86_64-qnx" + credential-helper: "scripts/internal/qnx_creds.py" + environment-name: "workflow-approval" + secrets: + score-qnx-license: ${{ secrets.SCORE_QNX_LICENSE }} + score-qnx-user: ${{ secrets.SCORE_QNX_USER }} + score-qnx-password: ${{ secrets.SCORE_QNX_PASSWORD }} + + qnx-build-arm64: uses: eclipse-score/cicd-workflows/.github/workflows/qnx-build.yml@main permissions: contents: read pull-requests: read with: - bazel-target: "//score/..." - bazel-config: "build_qnx8" + bazel-target: "//score/... //tests/..." + bazel-config: "arm64-qnx" credential-helper: "scripts/internal/qnx_creds.py" environment-name: "workflow-approval" secrets: diff --git a/MODULE.bazel b/MODULE.bazel index dfb0154..cfd7162 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -32,13 +32,12 @@ bazel_dep(name = "score_tooling", version = "1.0.4") bazel_dep(name = "score_rust_policies", version = "0.0.3") bazel_dep(name = "score_process", version = "1.4.0", dev_dependency = True) -bazel_dep(name = "score_platform", version = "0.5.1", dev_dependency = True) # This is main score repo +bazel_dep(name = "score_platform", version = "0.5.1", dev_dependency = True) # Toolchains and extensions bazel_dep(name = "score_toolchains_gcc", version = "0.5", dev_dependency = True) bazel_dep(name = "score_toolchains_qnx", version = "0.0.6", dev_dependency = True) -bazel_dep(name = "rust_qnx8_toolchain", version = "1.2.0", dev_dependency = True) -bazel_dep(name = "score_toolchains_rust", version = "0.1.1", dev_dependency = True) +bazel_dep(name = "score_toolchains_rust", version = "0.4.0", dev_dependency = True) # S-CORE crates bazel_dep(name = "score_crates", version = "0.0.6") @@ -50,20 +49,6 @@ git_override( remote = "https://github.com/eclipse-score/tooling.git", ) -git_override( - module_name = "score_toolchains_rust", - commit = "bcf8e5364f72cf136ec81960350a82e2b5c45449", - remote = "https://github.com/eclipse-score/toolchains_rust.git", -) - -archive_override( - module_name = "rust_qnx8_toolchain", - strip_prefix = "qnx8", - urls = [ - "https://github.com/qorix-group/rust-lang-qnx8/releases/download/1.2.0/qnx8_rust_toolchain.tar.gz", - ], -) - # Extensions gcc = use_extension("@score_toolchains_gcc//extentions:gcc.bzl", "gcc", dev_dependency = True) diff --git a/scripts/internal/qnx_creds.py b/scripts/internal/qnx_creds.py old mode 100644 new mode 100755 From 0bf5d4403f0424585beadf7660ac725b3768066a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20J=C4=99drzejewski?= <32277297+arkjedrz@users.noreply.github.com> Date: Fri, 30 Jan 2026 12:09:11 +0100 Subject: [PATCH 29/34] log: add QNX x86_64 config support (#50) * log: add QNX x86_64 config support New platforms must be explicitly added to FFI. * impl: fix unallocated array Unallocated array prevented build for QNX x86-64. --- score/datarouter/test/ut/ut_logging/test_logparser.cpp | 2 +- score/mw/log/rust/score_log_bridge/BUILD | 10 ++++++++++ score/mw/log/rust/score_log_bridge/src/adapter.cpp | 2 +- score/mw/log/rust/score_log_bridge/src/ffi.rs | 5 ++++- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/score/datarouter/test/ut/ut_logging/test_logparser.cpp b/score/datarouter/test/ut/ut_logging/test_logparser.cpp index dc7edc8..1f86258 100644 --- a/score/datarouter/test/ut/ut_logging/test_logparser.cpp +++ b/score/datarouter/test/ut/ut_logging/test_logparser.cpp @@ -70,7 +70,7 @@ template std::string make_message(S typeIndex, const T& t) { static constexpr msgsize_t MaxMessageSize = 65500; - std::array buffer; + std::array buffer{}; using s = ::score::common::visitor::logging_serializer; const auto indexSize = s::serialize(typeIndex, buffer.data(), buffer.size()); const auto size = indexSize + s::serialize(t, buffer.data() + indexSize, buffer.size() - indexSize); diff --git a/score/mw/log/rust/score_log_bridge/BUILD b/score/mw/log/rust/score_log_bridge/BUILD index 31523e1..45c6f1b 100644 --- a/score/mw/log/rust/score_log_bridge/BUILD +++ b/score/mw/log/rust/score_log_bridge/BUILD @@ -30,6 +30,14 @@ config_setting( ], ) +config_setting( + name = "x86_64-qnx", + constraint_values = [ + "@platforms//cpu:x86_64", + "@platforms//os:qnx", + ], +) + cc_library( name = "adapter", srcs = ["src/adapter.cpp"], @@ -37,6 +45,7 @@ cc_library( defines = select({ ":x86_64-linux": ["x86_64_linux"], ":arm64-qnx": ["arm64_qnx"], + ":x86_64-qnx": ["x86_64_qnx"], "//conditions:default": [], }), visibility = ["//visibility:private"], @@ -53,6 +62,7 @@ rust_library( crate_features = select({ ":x86_64-linux": ["x86_64_linux"], ":arm64-qnx": ["arm64_qnx"], + ":x86_64-qnx": ["x86_64_qnx"], "//conditions:default": [], }), edition = "2021", diff --git a/score/mw/log/rust/score_log_bridge/src/adapter.cpp b/score/mw/log/rust/score_log_bridge/src/adapter.cpp index 30e01a8..f46e4a0 100644 --- a/score/mw/log/rust/score_log_bridge/src/adapter.cpp +++ b/score/mw/log/rust/score_log_bridge/src/adapter.cpp @@ -23,7 +23,7 @@ using namespace score::mw::log::detail; // Those parameters must be: // - managed by build system (using defines and features) // - cross-checked between `ffi.rs` and `adapter.cpp` -#if defined(x86_64_linux) || defined(arm64_qnx) +#if defined(x86_64_linux) || defined(arm64_qnx) || defined(x86_64_qnx) // Expected size and alignment of `SlotHandle`. static_assert(sizeof(SlotHandle) == 24); static_assert(alignof(SlotHandle) == 8); diff --git a/score/mw/log/rust/score_log_bridge/src/ffi.rs b/score/mw/log/rust/score_log_bridge/src/ffi.rs index a896d89..6bef1da 100644 --- a/score/mw/log/rust/score_log_bridge/src/ffi.rs +++ b/score/mw/log/rust/score_log_bridge/src/ffi.rs @@ -181,12 +181,15 @@ unsafe impl Sync for Recorder {} /// Those parameters must be: /// - managed by build system (using defines and features) /// - cross-checked between `ffi.rs` and `adapter.cpp` -#[cfg(any(feature = "x86_64_linux", feature = "arm64_qnx"))] +#[cfg(any(feature = "x86_64_linux", feature = "arm64_qnx", feature = "x86_64_qnx"))] #[repr(C, align(8))] pub struct SlotHandleStorage { _private: [u8; 24], } +#[cfg(not(any(feature = "x86_64_linux", feature = "arm64_qnx", feature = "x86_64_qnx")))] +compile_error!("Unknown configuration, unable to check layout"); + impl SlotHandleStorage { /// Returns an unsafe mutable pointer to this object. pub fn as_mut_ptr(&mut self) -> *mut SlotHandleStorage { From 8ce9448e467ae33b0c1495644066034e140be139 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20J=C4=99drzejewski?= <32277297+arkjedrz@users.noreply.github.com> Date: Fri, 30 Jan 2026 12:32:33 +0100 Subject: [PATCH 30/34] log: add Rust log init for C++ (#38) - Add lib for log init when Rust lib is used by C++. - Add example. --- .../log/rust/score_log_bridge_cpp_init/BUILD | 67 +++++++++++++++++ .../examples/example_lib.rs | 75 +++++++++++++++++++ .../examples/main.cpp | 30 ++++++++ .../log/rust/score_log_bridge_cpp_init/ffi.rs | 50 +++++++++++++ .../score_log_bridge_init.cpp | 67 +++++++++++++++++ .../score_log_bridge_init.h | 65 ++++++++++++++++ 6 files changed, 354 insertions(+) create mode 100644 score/mw/log/rust/score_log_bridge_cpp_init/BUILD create mode 100644 score/mw/log/rust/score_log_bridge_cpp_init/examples/example_lib.rs create mode 100644 score/mw/log/rust/score_log_bridge_cpp_init/examples/main.cpp create mode 100644 score/mw/log/rust/score_log_bridge_cpp_init/ffi.rs create mode 100644 score/mw/log/rust/score_log_bridge_cpp_init/score_log_bridge_init.cpp create mode 100644 score/mw/log/rust/score_log_bridge_cpp_init/score_log_bridge_init.h diff --git a/score/mw/log/rust/score_log_bridge_cpp_init/BUILD b/score/mw/log/rust/score_log_bridge_cpp_init/BUILD new file mode 100644 index 0000000..99a9549 --- /dev/null +++ b/score/mw/log/rust/score_log_bridge_cpp_init/BUILD @@ -0,0 +1,67 @@ +# ******************************************************************************* +# 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("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library") +load("@rules_rust//rust:defs.bzl", "rust_static_library") + +rust_static_library( + name = "ffi", + srcs = ["ffi.rs"], + edition = "2021", + visibility = ["//visibility:private"], + deps = [ + "//score/mw/log/rust/score_log_bridge", + ], +) + +cc_library( + name = "score_log_bridge_cpp_init", + srcs = ["score_log_bridge_init.cpp"], + hdrs = ["score_log_bridge_init.h"], + include_prefix = "score/mw/log/rust", + visibility = ["//visibility:public"], + deps = [ + ":ffi", + # Link dependency required by `:ffi`. + "//score/mw/log/detail/common:recorder_factory", + ], +) + +# Example consists of Rust library, C++ library, C++ application. + +rust_static_library( + name = "example_lib", + srcs = ["examples/example_lib.rs"], + edition = "2021", + visibility = ["//visibility:private"], + deps = [ + "//score/mw/log/rust/score_log_bridge", + "@score_baselibs_rust//src/log/score_log", + ], +) + +cc_binary( + name = "example", + srcs = ["examples/main.cpp"], + linkopts = select({ + "@platforms//os:qnx": [ + "-lsocket", + ], + "//conditions:default": [], + }), + visibility = ["//visibility:public"], + deps = [ + ":example_lib", + ":score_log_bridge_cpp_init", + ], +) diff --git a/score/mw/log/rust/score_log_bridge_cpp_init/examples/example_lib.rs b/score/mw/log/rust/score_log_bridge_cpp_init/examples/example_lib.rs new file mode 100644 index 0000000..c541796 --- /dev/null +++ b/score/mw/log/rust/score_log_bridge_cpp_init/examples/example_lib.rs @@ -0,0 +1,75 @@ +// +// 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 +// +// +// SPDX-License-Identifier: Apache-2.0 +// + +//! Module contains functions printing example logs. +//! Based on `//score/mw/log/rust/score_log_bridge:example`. + +use score_log::{debug, error, fatal, info, trace, warn, Log}; +use score_log_bridge::ScoreLoggerBuilder; + +/// Show example logs. +#[no_mangle] +extern "C" fn show_logs() { + // Regular log usage. + trace!("This is a trace log - hidden"); + debug!("This is a debug log - hidden"); + info!("This is an info log"); + warn!("This is a warn log"); + error!("This is an error log"); + fatal!("This is a fatal log"); + + // Log with modified context. + trace!(context: "EX1", "This is a trace log - hidden"); + debug!(context: "EX1", "This is a debug log - hidden"); + info!(context: "EX1", "This is an info log"); + warn!(context: "EX1", "This is a warn log"); + error!(context: "EX1", "This is an error log"); + fatal!(context: "EX1", "This is a fatal log"); + + // Log with numeric values. + let x1 = 123.4; + let x2 = 111; + let x3 = true; + let x4 = -0x3Fi8; + error!( + "This is an error log with numeric values: {} {} {} {:x}", + x1, x2, x3, x4, + ); + + // Use logger instance with modified context. + let logger = ScoreLoggerBuilder::new() + .context("ALFA") + .show_module(false) + .show_file(true) + .show_line(false) + .build(); + + // Log with provided logger. + trace!( + logger: logger, + "This is a trace log - hidden" + ); + debug!(logger: logger, "This is a debug log - hidden"); + info!(logger: logger, "This is an info log"); + warn!(logger: logger, "This is a warn log"); + error!(logger: logger, "This is an error log"); + fatal!(logger: logger, "This is an fatal log"); + + // Log with provided logger and modified context. + trace!(logger: logger, context: "EX2", "This is a trace log - hidden"); + debug!(logger: logger, context: "EX2", "This is a debug log - hidden"); + info!(logger: logger, context: "EX2", "This is an info log"); + warn!(logger: logger, context: "EX2", "This is a warn log"); + error!(logger: logger, context: "EX2", "This is an error log"); + fatal!(logger: logger, context: "EX2", "This is an fatal log"); +} diff --git a/score/mw/log/rust/score_log_bridge_cpp_init/examples/main.cpp b/score/mw/log/rust/score_log_bridge_cpp_init/examples/main.cpp new file mode 100644 index 0000000..6528055 --- /dev/null +++ b/score/mw/log/rust/score_log_bridge_cpp_init/examples/main.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/rust/score_log_bridge_init.h" + +extern "C" { +void show_logs(); +} + +int main() +{ + using namespace score::mw::log::rust; + + ScoreLoggerBuilder builder; + builder.Context("ABCD").ShowModule(true).ShowFile(true).ShowLine(true).SetAsDefaultLogger(); + + show_logs(); + + return 0; +} diff --git a/score/mw/log/rust/score_log_bridge_cpp_init/ffi.rs b/score/mw/log/rust/score_log_bridge_cpp_init/ffi.rs new file mode 100644 index 0000000..59ea05b --- /dev/null +++ b/score/mw/log/rust/score_log_bridge_cpp_init/ffi.rs @@ -0,0 +1,50 @@ +// +// 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 +// +// +// SPDX-License-Identifier: Apache-2.0 +// + +use core::ffi::c_char; +use core::slice::from_raw_parts; +use score_log_bridge::ScoreLoggerBuilder; + +#[no_mangle] +extern "C" fn set_default_logger( + context_ptr: *const c_char, + context_size: usize, + show_module: *const bool, + show_file: *const bool, + show_line: *const bool, +) { + let mut builder = ScoreLoggerBuilder::new(); + + // Set parameters if non-null (option-like). + if !context_ptr.is_null() { + let context = unsafe { + let slice = from_raw_parts(context_ptr.cast(), context_size); + str::from_utf8(slice).expect("provided context is not a valid UTF-8 string") + }; + builder = builder.context(context); + } + + if !show_module.is_null() { + builder = builder.show_module(unsafe { *show_module }); + } + + if !show_file.is_null() { + builder = builder.show_file(unsafe { *show_file }); + } + + if !show_line.is_null() { + builder = builder.show_line(unsafe { *show_line }); + } + + builder.set_as_default_logger(); +} diff --git a/score/mw/log/rust/score_log_bridge_cpp_init/score_log_bridge_init.cpp b/score/mw/log/rust/score_log_bridge_cpp_init/score_log_bridge_init.cpp new file mode 100644 index 0000000..8bcd245 --- /dev/null +++ b/score/mw/log/rust/score_log_bridge_cpp_init/score_log_bridge_init.cpp @@ -0,0 +1,67 @@ +/******************************************************************************** + * 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_log_bridge_init.h" + +extern "C" void set_default_logger(const char* context_ptr, + size_t context_size, + const bool* show_module, + const bool* show_file, + const bool* show_line); + +namespace score::mw::log::rust +{ + +ScoreLoggerBuilder& ScoreLoggerBuilder::Context(const std::string& context) noexcept +{ + context_ = context; + return *this; +} + +ScoreLoggerBuilder& ScoreLoggerBuilder::ShowModule(bool show_module) noexcept +{ + show_module_ = show_module; + return *this; +} + +ScoreLoggerBuilder& ScoreLoggerBuilder::ShowFile(bool show_file) noexcept +{ + show_file_ = show_file; + return *this; +} + +ScoreLoggerBuilder& ScoreLoggerBuilder::ShowLine(bool show_line) noexcept +{ + show_line_ = show_line; + return *this; +} + +void ScoreLoggerBuilder::SetAsDefaultLogger() noexcept +{ + const char* context_ptr{nullptr}; + size_t context_size{0}; + if (context_) + { + auto value{context_.value()}; + context_ptr = value.c_str(); + context_size = value.size(); + } + + const bool* show_module{show_module_ ? &show_module_.value() : nullptr}; + const bool* show_file{show_file_ ? &show_file_.value() : nullptr}; + const bool* show_line{show_line_ ? &show_line_.value() : nullptr}; + + set_default_logger(context_ptr, context_size, show_module, show_file, show_line); +} + +} // namespace score::mw::log::rust diff --git a/score/mw/log/rust/score_log_bridge_cpp_init/score_log_bridge_init.h b/score/mw/log/rust/score_log_bridge_cpp_init/score_log_bridge_init.h new file mode 100644 index 0000000..05439ca --- /dev/null +++ b/score/mw/log/rust/score_log_bridge_cpp_init/score_log_bridge_init.h @@ -0,0 +1,65 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ + +#pragma once + +#include +#include + +namespace score::mw::log::rust +{ + +/// @brief +/// Builder for logger used by Rust libraries. +/// +/// @note +/// If parameter is not set explicitly then Rust-side default is used. +/// Only global logger setup is allowed. +/// `config` method is not exposed. +class ScoreLoggerBuilder final +{ + public: + /// @brief Set context for the logger. + /// @param context + /// Context name. + /// Only ASCII characters are allowed. + /// Max 4 characters are used. Rest of the provided string will be trimmed. + /// @return This builder. + ScoreLoggerBuilder& Context(const std::string& context) noexcept; + + /// @brief Show module name in logs. + /// @param show_module Value to set. + /// @return This builder. + ScoreLoggerBuilder& ShowModule(bool show_module) noexcept; + + /// @brief Show file name in logs. + /// @param show_module Value to set. + /// @return This builder. + ScoreLoggerBuilder& ShowFile(bool show_file) noexcept; + + /// @brief Show line number in logs. + /// @param show_module Value to set. + /// @return This builder. + ScoreLoggerBuilder& ShowLine(bool show_line) noexcept; + + /// @brief Initialize default logger with provided parameters. + void SetAsDefaultLogger() noexcept; + + private: + std::optional context_; + std::optional show_module_; + std::optional show_file_; + std::optional show_line_; +}; + +} // namespace score::mw::log::rust From b0bfd673269cc7b48b4ddfff3b70c64b3ab13d0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20J=C4=99drzejewski?= <32277297+arkjedrz@users.noreply.github.com> Date: Fri, 30 Jan 2026 15:29:39 +0100 Subject: [PATCH 31/34] log: rename `ScoreLogger` to `ScoreLogBridge` (#49) Fix naming within code. --- .../rust/score_log_bridge/examples/main.rs | 8 ++-- score/mw/log/rust/score_log_bridge/src/lib.rs | 4 +- .../{score_logger.rs => score_log_bridge.rs} | 46 +++++++++---------- .../examples/example_lib.rs | 4 +- .../examples/main.cpp | 2 +- .../log/rust/score_log_bridge_cpp_init/ffi.rs | 4 +- .../score_log_bridge_init.cpp | 10 ++-- .../score_log_bridge_init.h | 10 ++-- 8 files changed, 44 insertions(+), 44 deletions(-) rename score/mw/log/rust/score_log_bridge/src/{score_logger.rs => score_log_bridge.rs} (89%) diff --git a/score/mw/log/rust/score_log_bridge/examples/main.rs b/score/mw/log/rust/score_log_bridge/examples/main.rs index 8c1c928..bd96a25 100644 --- a/score/mw/log/rust/score_log_bridge/examples/main.rs +++ b/score/mw/log/rust/score_log_bridge/examples/main.rs @@ -12,7 +12,7 @@ // use score_log::{debug, error, fatal, info, trace, warn, Log}; -use score_log_bridge::ScoreLoggerBuilder; +use score_log_bridge::ScoreLogBridgeBuilder; use std::path::PathBuf; fn main() { @@ -24,8 +24,8 @@ fn main() { .join("config") .join("logging.json"); - // Initialize `ScoreLogger` as a default logger. - ScoreLoggerBuilder::new() + // Initialize `ScoreLogBridge` as a default logger. + ScoreLogBridgeBuilder::new() .show_module(false) .show_file(true) .show_line(false) @@ -59,7 +59,7 @@ fn main() { ); // Use logger instance with modified context. - let logger = ScoreLoggerBuilder::new() + let logger = ScoreLogBridgeBuilder::new() .context("ALFA") .show_module(false) .show_file(true) diff --git a/score/mw/log/rust/score_log_bridge/src/lib.rs b/score/mw/log/rust/score_log_bridge/src/lib.rs index 0dfd5cc..553a421 100644 --- a/score/mw/log/rust/score_log_bridge/src/lib.rs +++ b/score/mw/log/rust/score_log_bridge/src/lib.rs @@ -18,6 +18,6 @@ #![warn(clippy::alloc_instead_of_core)] mod ffi; -mod score_logger; +mod score_log_bridge; -pub use crate::score_logger::{ScoreLogger, ScoreLoggerBuilder}; +pub use crate::score_log_bridge::{ScoreLogBridge, ScoreLogBridgeBuilder}; diff --git a/score/mw/log/rust/score_log_bridge/src/score_logger.rs b/score/mw/log/rust/score_log_bridge/src/score_log_bridge.rs similarity index 89% rename from score/mw/log/rust/score_log_bridge/src/score_logger.rs rename to score/mw/log/rust/score_log_bridge/src/score_log_bridge.rs index 9b4e3af..4ee0a41 100644 --- a/score/mw/log/rust/score_log_bridge/src/score_logger.rs +++ b/score/mw/log/rust/score_log_bridge/src/score_log_bridge.rs @@ -19,8 +19,8 @@ use score_log::{Log, Metadata, Record}; use std::env::{set_var, var_os}; use std::path::PathBuf; -/// Builder for the [`ScoreLogger`]. -pub struct ScoreLoggerBuilder { +/// Builder for the [`ScoreLogBridge`]. +pub struct ScoreLogBridgeBuilder { context: Context, show_module: bool, show_file: bool, @@ -28,7 +28,7 @@ pub struct ScoreLoggerBuilder { config_path: Option, } -impl ScoreLoggerBuilder { +impl ScoreLogBridgeBuilder { /// Create builder with default parameters. /// /// # Panics @@ -39,7 +39,7 @@ impl ScoreLoggerBuilder { Self::default() } - /// Set context for the [`ScoreLogger`]. + /// Set context for the [`ScoreLogBridge`]. /// /// Only ASCII characters are allowed. /// Max 4 characters are used. Rest of the provided string will be trimmed. @@ -77,10 +77,10 @@ impl ScoreLoggerBuilder { self } - /// Build the [`ScoreLogger`] with provided context and configuration. - pub fn build(self) -> ScoreLogger { + /// Build the [`ScoreLogBridge`] with provided context and configuration. + pub fn build(self) -> ScoreLogBridge { let recorder = Recorder::new(); - ScoreLogger { + ScoreLogBridge { context: self.context, show_module: self.show_module, show_file: self.show_file, @@ -89,7 +89,7 @@ impl ScoreLoggerBuilder { } } - /// Build the [`ScoreLogger`] and set it as the default logger. + /// Build the [`ScoreLogBridge`] and set it as the default logger. /// /// # Safety /// @@ -126,7 +126,7 @@ impl ScoreLoggerBuilder { } } -impl Default for ScoreLoggerBuilder { +impl Default for ScoreLogBridgeBuilder { /// Create builder with default parameters. /// /// # Panics @@ -154,7 +154,7 @@ impl Default for ScoreLoggerBuilder { } /// C++-based logger implementation. -pub struct ScoreLogger { +pub struct ScoreLogBridge { context: Context, show_module: bool, show_file: bool, @@ -162,14 +162,14 @@ pub struct ScoreLogger { recorder: Recorder, } -impl ScoreLogger { +impl ScoreLogBridge { /// Current log level for provided context. pub(crate) fn log_level(&self, context: &Context) -> LogLevel { self.recorder.log_level(context) } } -impl Log for ScoreLogger { +impl Log for ScoreLogBridge { fn enabled(&self, metadata: &Metadata) -> bool { let context = Context::from(metadata.context()); self.log_level(&context) >= metadata.level().into() @@ -222,13 +222,13 @@ impl Log for ScoreLogger { #[cfg(test)] mod tests { - use crate::ScoreLoggerBuilder; + use crate::ScoreLogBridgeBuilder; use std::env::var_os; use std::path::PathBuf; #[test] fn test_builder_new() { - let builder = ScoreLoggerBuilder::new(); + let builder = ScoreLogBridgeBuilder::new(); assert_eq!(builder.context, "DFLT".into()); assert!(!builder.show_module); assert!(!builder.show_file); @@ -238,7 +238,7 @@ mod tests { #[test] fn test_builder_default() { - let builder = ScoreLoggerBuilder::default(); + let builder = ScoreLogBridgeBuilder::default(); assert_eq!(builder.context, "DFLT".into()); assert!(!builder.show_module); assert!(!builder.show_file); @@ -248,7 +248,7 @@ mod tests { #[test] fn test_builder_context() { - let builder = ScoreLoggerBuilder::new().context("NEW_CONTEXT"); + let builder = ScoreLogBridgeBuilder::new().context("NEW_CONTEXT"); assert_eq!(builder.context, "NEW_CONTEXT".into()); assert!(!builder.show_module); assert!(!builder.show_file); @@ -258,7 +258,7 @@ mod tests { #[test] fn test_builder_show_module() { - let builder = ScoreLoggerBuilder::new().show_module(true); + let builder = ScoreLogBridgeBuilder::new().show_module(true); assert_eq!(builder.context, "DFLT".into()); assert!(builder.show_module); assert!(!builder.show_file); @@ -268,7 +268,7 @@ mod tests { #[test] fn test_builder_show_file() { - let builder = ScoreLoggerBuilder::new().show_file(true); + let builder = ScoreLogBridgeBuilder::new().show_file(true); assert_eq!(builder.context, "DFLT".into()); assert!(!builder.show_module); assert!(builder.show_file); @@ -278,7 +278,7 @@ mod tests { #[test] fn test_builder_show_line() { - let builder = ScoreLoggerBuilder::new().show_line(true); + let builder = ScoreLogBridgeBuilder::new().show_line(true); assert_eq!(builder.context, "DFLT".into()); assert!(!builder.show_module); assert!(!builder.show_file); @@ -288,7 +288,7 @@ mod tests { #[test] fn test_builder_config_path() { - let builder = ScoreLoggerBuilder::new().config(PathBuf::from("/some/path")); + let builder = ScoreLogBridgeBuilder::new().config(PathBuf::from("/some/path")); assert_eq!(builder.context, "DFLT".into()); assert!(!builder.show_module); assert!(!builder.show_file); @@ -298,7 +298,7 @@ mod tests { #[test] fn test_builder_chained() { - let builder = ScoreLoggerBuilder::new() + let builder = ScoreLogBridgeBuilder::new() .context("NEW_CONTEXT") .show_module(true) .show_file(true) @@ -313,7 +313,7 @@ mod tests { #[test] fn test_builder_build() { - let logger = ScoreLoggerBuilder::new() + let logger = ScoreLogBridgeBuilder::new() .context("NEW_CONTEXT") .show_module(true) .show_file(true) @@ -332,7 +332,7 @@ mod tests { assert!(var_os(KEY).is_none()); let config_path = PathBuf::from("/some/path"); - ScoreLoggerBuilder::new() + ScoreLogBridgeBuilder::new() .context("NEW_CONTEXT") .show_module(true) .show_file(true) diff --git a/score/mw/log/rust/score_log_bridge_cpp_init/examples/example_lib.rs b/score/mw/log/rust/score_log_bridge_cpp_init/examples/example_lib.rs index c541796..eb7be30 100644 --- a/score/mw/log/rust/score_log_bridge_cpp_init/examples/example_lib.rs +++ b/score/mw/log/rust/score_log_bridge_cpp_init/examples/example_lib.rs @@ -15,7 +15,7 @@ //! Based on `//score/mw/log/rust/score_log_bridge:example`. use score_log::{debug, error, fatal, info, trace, warn, Log}; -use score_log_bridge::ScoreLoggerBuilder; +use score_log_bridge::ScoreLogBridgeBuilder; /// Show example logs. #[no_mangle] @@ -47,7 +47,7 @@ extern "C" fn show_logs() { ); // Use logger instance with modified context. - let logger = ScoreLoggerBuilder::new() + let logger = ScoreLogBridgeBuilder::new() .context("ALFA") .show_module(false) .show_file(true) diff --git a/score/mw/log/rust/score_log_bridge_cpp_init/examples/main.cpp b/score/mw/log/rust/score_log_bridge_cpp_init/examples/main.cpp index 6528055..43ce02e 100644 --- a/score/mw/log/rust/score_log_bridge_cpp_init/examples/main.cpp +++ b/score/mw/log/rust/score_log_bridge_cpp_init/examples/main.cpp @@ -21,7 +21,7 @@ int main() { using namespace score::mw::log::rust; - ScoreLoggerBuilder builder; + ScoreLogBridgeBuilder builder; builder.Context("ABCD").ShowModule(true).ShowFile(true).ShowLine(true).SetAsDefaultLogger(); show_logs(); diff --git a/score/mw/log/rust/score_log_bridge_cpp_init/ffi.rs b/score/mw/log/rust/score_log_bridge_cpp_init/ffi.rs index 59ea05b..eb13677 100644 --- a/score/mw/log/rust/score_log_bridge_cpp_init/ffi.rs +++ b/score/mw/log/rust/score_log_bridge_cpp_init/ffi.rs @@ -13,7 +13,7 @@ use core::ffi::c_char; use core::slice::from_raw_parts; -use score_log_bridge::ScoreLoggerBuilder; +use score_log_bridge::ScoreLogBridgeBuilder; #[no_mangle] extern "C" fn set_default_logger( @@ -23,7 +23,7 @@ extern "C" fn set_default_logger( show_file: *const bool, show_line: *const bool, ) { - let mut builder = ScoreLoggerBuilder::new(); + let mut builder = ScoreLogBridgeBuilder::new(); // Set parameters if non-null (option-like). if !context_ptr.is_null() { diff --git a/score/mw/log/rust/score_log_bridge_cpp_init/score_log_bridge_init.cpp b/score/mw/log/rust/score_log_bridge_cpp_init/score_log_bridge_init.cpp index 8bcd245..598b34b 100644 --- a/score/mw/log/rust/score_log_bridge_cpp_init/score_log_bridge_init.cpp +++ b/score/mw/log/rust/score_log_bridge_cpp_init/score_log_bridge_init.cpp @@ -22,31 +22,31 @@ extern "C" void set_default_logger(const char* context_ptr, namespace score::mw::log::rust { -ScoreLoggerBuilder& ScoreLoggerBuilder::Context(const std::string& context) noexcept +ScoreLogBridgeBuilder& ScoreLogBridgeBuilder::Context(const std::string& context) noexcept { context_ = context; return *this; } -ScoreLoggerBuilder& ScoreLoggerBuilder::ShowModule(bool show_module) noexcept +ScoreLogBridgeBuilder& ScoreLogBridgeBuilder::ShowModule(bool show_module) noexcept { show_module_ = show_module; return *this; } -ScoreLoggerBuilder& ScoreLoggerBuilder::ShowFile(bool show_file) noexcept +ScoreLogBridgeBuilder& ScoreLogBridgeBuilder::ShowFile(bool show_file) noexcept { show_file_ = show_file; return *this; } -ScoreLoggerBuilder& ScoreLoggerBuilder::ShowLine(bool show_line) noexcept +ScoreLogBridgeBuilder& ScoreLogBridgeBuilder::ShowLine(bool show_line) noexcept { show_line_ = show_line; return *this; } -void ScoreLoggerBuilder::SetAsDefaultLogger() noexcept +void ScoreLogBridgeBuilder::SetAsDefaultLogger() noexcept { const char* context_ptr{nullptr}; size_t context_size{0}; diff --git a/score/mw/log/rust/score_log_bridge_cpp_init/score_log_bridge_init.h b/score/mw/log/rust/score_log_bridge_cpp_init/score_log_bridge_init.h index 05439ca..8df157d 100644 --- a/score/mw/log/rust/score_log_bridge_cpp_init/score_log_bridge_init.h +++ b/score/mw/log/rust/score_log_bridge_cpp_init/score_log_bridge_init.h @@ -26,7 +26,7 @@ namespace score::mw::log::rust /// If parameter is not set explicitly then Rust-side default is used. /// Only global logger setup is allowed. /// `config` method is not exposed. -class ScoreLoggerBuilder final +class ScoreLogBridgeBuilder final { public: /// @brief Set context for the logger. @@ -35,22 +35,22 @@ class ScoreLoggerBuilder final /// Only ASCII characters are allowed. /// Max 4 characters are used. Rest of the provided string will be trimmed. /// @return This builder. - ScoreLoggerBuilder& Context(const std::string& context) noexcept; + ScoreLogBridgeBuilder& Context(const std::string& context) noexcept; /// @brief Show module name in logs. /// @param show_module Value to set. /// @return This builder. - ScoreLoggerBuilder& ShowModule(bool show_module) noexcept; + ScoreLogBridgeBuilder& ShowModule(bool show_module) noexcept; /// @brief Show file name in logs. /// @param show_module Value to set. /// @return This builder. - ScoreLoggerBuilder& ShowFile(bool show_file) noexcept; + ScoreLogBridgeBuilder& ShowFile(bool show_file) noexcept; /// @brief Show line number in logs. /// @param show_module Value to set. /// @return This builder. - ScoreLoggerBuilder& ShowLine(bool show_line) noexcept; + ScoreLogBridgeBuilder& ShowLine(bool show_line) noexcept; /// @brief Initialize default logger with provided parameters. void SetAsDefaultLogger() noexcept; From 4605c13524e87ce5b05c4e3a3335878e6938801f Mon Sep 17 00:00:00 2001 From: Raghavendra Maddikery Date: Mon, 2 Feb 2026 09:41:38 +0100 Subject: [PATCH 32/34] Project import generated by Copybara. GIT_ORIGIN_SPP_REV_ID: 200c85e6bc82deb5bef26dc0f6122c9b0090b27d --- score/datarouter/BUILD | 49 +- .../build_configuration_flags/BUILD | 6 +- score/datarouter/daemon_communication/BUILD | 12 + .../session_handle_interface.h | 2 +- .../session_handle_mock.h | 2 +- score/datarouter/datarouter/data_router.cpp | 58 +- score/datarouter/datarouter/data_router.h | 2 +- .../dlt_filetransfer_trigger_lib/BUILD | 12 + .../filetransfer_message.h | 2 +- .../include/file_transfer.h | 6 +- .../include/ifile_transfer.h | 6 +- .../config_session_factory/BUILD | 12 + .../config_session_stub/BUILD | 12 + .../dynamic_configuration/i_session/BUILD | 12 + score/datarouter/error/BUILD | 12 + score/datarouter/error/error.cpp | 4 +- score/datarouter/error/error.h | 6 +- .../applications/datarouter_feature_config.h | 8 +- .../include/daemon/dlt_log_server.h | 2 +- .../include/daemon/message_passing_server.h | 49 +- .../datarouter/include/daemon/socketserver.h | 50 ++ .../include/unix_domain/unix_domain_common.h | 2 +- score/datarouter/lib/synchronized/BUILD | 12 + .../lib/synchronized/synchronized.h | 24 +- .../lib/synchronized/synchronized_test.cpp | 116 ++-- score/datarouter/network/BUILD | 12 + score/datarouter/network/vlan_local.h | 4 + score/datarouter/nonverbose_dlt_stub/BUILD | 12 + .../nonverbose_dlt_stub/stub_nonverbose_dlt.h | 2 +- .../src/applications/main_nonadaptive.cpp | 22 +- .../datarouter/src/daemon/dlt_log_server.cpp | 2 +- .../src/daemon/message_passing_server.cpp | 237 ++++---- score/datarouter/src/daemon/socketserver.cpp | 41 +- .../src/daemon/socketserver_config.cpp | 8 +- .../datarouter/{ => src}/file_transfer/BUILD | 2 +- .../file_transfer_handler_factory.hpp | 0 .../file_transfer/file_transfer_stub/BUILD | 2 +- .../file_transfer_handler_factory_stub.h | 4 +- .../file_transfer_stream_handler_stub.h | 0 score/datarouter/src/persistency/BUILD | 12 + .../src/persistency/i_persistent_dictionary.h | 8 +- .../persistency/mock_persistent_dictionary.h | 8 +- .../persistent_dictionary_factory.hpp | 6 +- .../stub_persistent_dictionary.cpp | 12 +- .../stub_persistent_dictionary.h | 8 +- .../{ => src}/persistent_logging/BUILD | 16 +- .../persistent_logging/isysedr_handler.h | 6 +- .../persistent_logging_stub/BUILD | 4 +- .../stub_sysedr_factory.cpp | 2 +- .../stub_sysedr_factory.h | 10 +- .../stub_sysedr_handler.cpp | 4 +- .../stub_sysedr_handler.h | 10 +- .../persistent_logging/sysedr_factory.hpp | 8 +- .../src/unix_domain/unix_domain_server.cpp | 18 +- score/datarouter/test/ut/ut_logging/BUILD | 15 +- .../test/ut/ut_logging/test_dltserver.cpp | 400 ++++-------- .../test_file_transfer_handler_factory.cpp | 6 +- .../ut_logging/test_filetransfer_stream.cpp | 2 +- .../test_message_passing_server.cpp | 376 ++++++++---- .../test/ut/ut_logging/test_nonverbosedlt.cpp | 6 +- .../test/ut/ut_logging/test_socketserver.cpp | 116 +++- .../ut_logging/test_socketserver_config.cpp | 18 +- score/mw/log/BUILD | 8 + score/mw/log/detail/data_router/BUILD | 10 +- ...ata_router_message_client_factory_test.cpp | 2 - .../data_router_message_client_impl.cpp | 250 ++++---- .../data_router_message_client_impl.h | 46 +- .../data_router_message_client_test.cpp | 567 +++++++++++++----- .../data_router/data_router_messages.cpp | 5 - .../detail/data_router/data_router_messages.h | 39 +- .../data_router/message_passing_config.h | 63 ++ .../data_router/message_passing_factory.h | 28 +- .../message_passing_factory_impl.cpp | 31 +- .../message_passing_factory_impl.h | 61 +- .../message_passing_factory_mock.h | 22 +- .../message_passing_factory_test.cpp | 19 +- score/mw/log/detail/flags/BUILD | 16 - .../mw/log/detail/utils/signal_handling/BUILD | 37 ++ .../utils/signal_handling/signal_handling.cpp | 59 ++ .../utils/signal_handling/signal_handling.h | 58 ++ score/mw/log/legacy_non_verbose_api/BUILD | 1 - .../legacy_non_verbose_api/tracing_test.cpp | 70 +++ .../console_logging_environment.cpp | 8 - score/mw/log/test/fake_recorder/BUILD | 28 + score/mw/log/test/fake_recorder/README.md | 65 ++ .../log/test/fake_recorder/fake_recorder.cpp | 251 ++++++++ .../mw/log/test/fake_recorder/fake_recorder.h | 99 +++ .../log/test/fake_recorder_environment/BUILD | 41 ++ .../fake_recorder_environment.cpp | 47 ++ .../fake_recorder_environment.h | 55 ++ .../register_fake_recorder_environment.cpp | 30 + 91 files changed, 2712 insertions(+), 1201 deletions(-) rename score/datarouter/{ => src}/file_transfer/BUILD (94%) rename score/datarouter/{ => src}/file_transfer/file_transfer_handler_factory.hpp (100%) rename score/datarouter/{ => src}/file_transfer/file_transfer_stub/BUILD (95%) rename score/datarouter/{ => src}/file_transfer/file_transfer_stub/file_transfer_handler_factory_stub.h (91%) rename score/datarouter/{ => src}/file_transfer/file_transfer_stub/file_transfer_stream_handler_stub.h (100%) rename score/datarouter/{ => src}/persistent_logging/BUILD (64%) rename score/datarouter/{ => src}/persistent_logging/isysedr_handler.h (86%) rename score/datarouter/{ => src}/persistent_logging/persistent_logging_stub/BUILD (86%) rename score/datarouter/{ => src}/persistent_logging/persistent_logging_stub/stub_sysedr_factory.cpp (91%) rename score/datarouter/{ => src}/persistent_logging/persistent_logging_stub/stub_sysedr_factory.h (65%) rename score/datarouter/{ => src}/persistent_logging/persistent_logging_stub/stub_sysedr_handler.cpp (81%) rename score/datarouter/{ => src}/persistent_logging/persistent_logging_stub/stub_sysedr_handler.h (66%) rename score/datarouter/{ => src}/persistent_logging/sysedr_factory.hpp (84%) create mode 100644 score/mw/log/detail/data_router/message_passing_config.h create mode 100644 score/mw/log/detail/utils/signal_handling/BUILD create mode 100644 score/mw/log/detail/utils/signal_handling/signal_handling.cpp create mode 100644 score/mw/log/detail/utils/signal_handling/signal_handling.h create mode 100644 score/mw/log/test/fake_recorder/BUILD create mode 100644 score/mw/log/test/fake_recorder/README.md create mode 100644 score/mw/log/test/fake_recorder/fake_recorder.cpp create mode 100644 score/mw/log/test/fake_recorder/fake_recorder.h create mode 100644 score/mw/log/test/fake_recorder_environment/BUILD create mode 100644 score/mw/log/test/fake_recorder_environment/fake_recorder_environment.cpp create mode 100644 score/mw/log/test/fake_recorder_environment/fake_recorder_environment.h create mode 100644 score/mw/log/test/fake_recorder_environment/register_fake_recorder_environment.cpp 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..15792a6 --- /dev/null +++ b/score/mw/log/detail/utils/signal_handling/signal_handling.cpp @@ -0,0 +1,59 @@ +/******************************************************************************** + * Copyright (c) 2026 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..b8aa8cf --- /dev/null +++ b/score/mw/log/detail/utils/signal_handling/signal_handling.h @@ -0,0 +1,58 @@ +/******************************************************************************** + * Copyright (c) 2026 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 From b474827a485f8601914be15be1b912d76909330a Mon Sep 17 00:00:00 2001 From: Raghavendra Maddikery Date: Fri, 30 Jan 2026 21:24:58 +0100 Subject: [PATCH 33/34] Fixes feature flag name change for file_transfer --- .bazelrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 0cb15118eafd598f3d73d3613ab41b8be0eb9c76 Mon Sep 17 00:00:00 2001 From: Raghavendra Maddikery Date: Fri, 30 Jan 2026 21:25:44 +0100 Subject: [PATCH 34/34] Bumps baselibs and communication module deps --- MODULE.bazel | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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", )