Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
188 changes: 188 additions & 0 deletions docs/components/mw_log/detailed_design/_assets/mw_log_recorders.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
' *******************************************************************************
' 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
' *******************************************************************************

@startuml mw_log_recorders

' NOTE: this is a manual copy of the source of truth at
' score/mw/log/design/backend/mw_log_recorders.puml. Keep both in sync.

' Interface
interface "mw::log::Recorder" as Recorder {
+ StartRecord(ctx:std::string_view, log_level:LogLevel) : score::cpp::optional<SlotHandle>
+ StopRecord(slot:SlotHandle&): void
+ Log(const SlotHandle&, const T data) - family of functions
+ IsLogEnabled(const LogLevel&, const std::string_view): bool
}

' Interface
interface "mw::log::detail::Backend" as Backend {
+ ReserveSlot() : score::cpp::optional<SlotHandle>
+ FlushSlot(slot : const SlotHandle&)
+ GetLogRecord(slot : const SlotHandle&): LogRecord&
}

' Recorder implementations
class "mw::log::detail::FileRecorder" as FileRecorder {
- backend_ : std::unique_ptr<Backend>
- config_ : Configuration
__
+ FileRecorder(const detail::Configuration&,\n std::unique_ptr<detail::Backend>)
+ StartRecord(ctx:std::string_view, const LogLevel): score::cpp::optional<SlotHandle>
+ StopRecord(const SlotHandle&): void
+ IsLogEnabled(const LogLevel&, const std::string_view): bool
+ Log(const SlotHandle&, T data) - family of functions
}

class "mw::log::detail::TextRecorder" as TextRecorder {
- backend_ : std::unique_ptr<Backend>
- config_ : Configuration
- check_log_level_for_console_: bool
__
+ TextRecorder(const detail::Configuration&,\n std::unique_ptr<detail::Backend>,\n const bool)
+ StartRecord(ctx:std::string_view, const LogLevel): score::cpp::optional<SlotHandle>
+ StopRecord(const SlotHandle&): void
+ IsLogEnabled(const LogLevel&, const std::string_view): bool
+ Log(const SlotHandle&, T data) - family of functions
}

class "mw::log::detail::DataRouterRecorder" as DataRouterRecorder {
}

class "mw::log::detail::CompositeRecorder" as CompositeRecorder {
- recorders_ : std::vector<std::unique_ptr<Recorder>>
__
+ CompositeRecorder(std::vector<std::unique_ptr<Recorder>> recorders)
+ StartRecord(const std::string_view, const LogLevel): score::cpp::optional<SlotHandle>
+ StopRecord(slot:SlotHandle&): void
+ GetRecorders(): std::vector<std::unique_ptr<Recorder>>&
+ IsLogEnabled(const LogLevel&, const std::string_view): bool
+ Log(const SlotHandle&, const T) - family of functions
}

class "mw::log::detail::EmptyRecorder" as EmptyRecorder {
+ StartRecord(const std::string_view, const LogLevel):\n score::cpp::optional<SlotHandle>
+ StopRecord(const SlotHandle&): void
+ IsLogEnabled(const LogLevel&, const std::string_view): bool
+ Log(const SlotHandle&, T data) - family of functions
}

class "mw::log::detail::RecorderMock" as RecorderMock #pink {
}

' Backend implementations
class "mw::log::detail::FileOutputBackend" as FileOutputBackend {
}

class "mw::log::detail::SlogBackend" as SlogBackend {
- app_id_: std::string
- buffer_: CircularAllocator<mw::log::detail::LogRecord>
- slog_buffer_: slog2_buffer_t
- slog_buffer_config_: slog2_buffer_set_config_t
- slog2_instance_: std::unique_ptr<score::os::qnx::Slog2>
__
+ SlogBackend(const std::size_t,\n const LogRecord&,\n const std::string_view,\n std::unique_ptr<score::os::qnx::Slog2>)
+ ReserveSlot(): score::cpp::optional<SlotHandle>
+ FlushSlot(const SlotHandle&): void
+ GetLogRecord(const SlotHandle&): LogRecord&
__
- Init(verbosity: std::uint8_t) : void
}

class "mw::log::detail::DataRouterBackend" as DataRouterBackend {
}

class "mw::log::detail::BackendMock" as BackendMock #pink {
}

' Helper classes
class "DltArgumentCounter" as DltArgumentCounter {
- counter_ : std::uint8_t&
__
DltArgumentCounter(std::uint8_t&)
__
+ TryAddArgument(add_argument_callback) : AddArgumentResult
}

class "mw::log::detail::TextFormat" as TextFormat {
{static} + PutFormattedTime(VerbosePayload&) : void
{static} + TerminateLog(VerbosePayload&) : void
{static} + Log(VerbosePayload&, const T, const IntegerRepresentation) : void\n - family of functions
{static} + Log(VerbosePayload&, const T) : void\n - family of functions
}

class "mw::log::detail::DLTFormat" as DLTFormat {
+ Log(VerbosePayload&, const bool): AddArgumentResult
+ Log(VerbosePayload&, T, const IntegerRepresentation):\n AddArgumentResult - family of functions
}

class "mw::log::detail::CircularAllocator<T>" as CircularAllocator {
}

class "mw::log::detail::LogRecord" as LogRecord {
}

' External dependencies
class "OSAL::fcntl" as fcntl {
}

class "OSAL::Unistd" as Unistd {
}

' Relationships - Recorder interface
FileRecorder .up.|> Recorder
TextRecorder .up.|> Recorder
DataRouterRecorder .up.|> Recorder
CompositeRecorder .up.|> Recorder
EmptyRecorder .up.|> Recorder
RecorderMock .up.|> Recorder

' Relationships - Backend interface
FileOutputBackend .up.|> Backend
SlogBackend .up.|> Backend
DataRouterBackend .up.|> Backend
BackendMock .up.|> Backend

' Composition relationships
FileRecorder *-- Backend
FileRecorder *-- DltArgumentCounter
TextRecorder *-- Backend
TextRecorder *-- DltArgumentCounter
DataRouterRecorder *-- Backend

' Dependencies
FileRecorder ..> TextFormat : Log
FileRecorder ..> DLTFormat : uses
TextRecorder ..> TextFormat : Log
DataRouterRecorder ..> DLTFormat : uses
TextRecorder ..> DLTFormat : uses

' Backend composition
SlogBackend *-- CircularAllocator
SlogBackend --> LogRecord : uses

' External dependencies
FileRecorder ..> fcntl : open\nSetNonBlocking / fctrl call
FileRecorder ..> Unistd : close

' Notes
note bottom of Recorder
Refer Recorder.h for all the Log(const SlotHandle&, const T data)
- family of functions
end note

note top of TextFormat
Refer text_format.h for all the Log(...)
- family of functions
end note

@enduml
2 changes: 1 addition & 1 deletion docs/components/mw_log/detailed_design/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Detailed Design

The backend composition and recorder relationships are shown below:

.. uml:: ../../../../score/mw/log/design/backend/mw_log_recorders.puml
.. uml:: _assets/mw_log_recorders.puml

.. toctree::
:maxdepth: 1
Expand Down
20 changes: 13 additions & 7 deletions docs/verification_report/stats.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,19 @@ Quality Stats
Overview
--------

*Scoped to requirements owned by this repository (this repo's own*
``comp_req`` *needs). Requirements pulled in from external repositories
(score_baselibs, score_platform, score_process) that this project links
against for traceability purposes are excluded, since they are not owned
or maintained here.*

.. needpie:: Requirements Status
:labels: not valid, valid but not tested, valid and tested
:colors: red,yellow, green

type == 'comp_req' and status == 'invalid'
type == 'comp_req' and testlink == '' and (status == 'valid' or status == 'invalid')
type == 'comp_req' and testlink != '' and (status == 'valid' or status == 'invalid')
type == 'comp_req' and is_external == False and status == 'invalid'
type == 'comp_req' and is_external == False and testlink == '' and (status == 'valid' or status == 'invalid')
type == 'comp_req' and is_external == False and testlink != '' and (status == 'valid' or status == 'invalid')

In Detail
---------
Expand All @@ -40,17 +46,17 @@ In Detail
:labels: not valid, valid
:colors: red, orange, green

type == 'comp_req' and status == 'invalid'
type == 'comp_req' and status == 'valid'
type == 'comp_req' and is_external == False and status == 'invalid'
type == 'comp_req' and is_external == False and status == 'valid'

.. grid-item-card::

.. needpie:: Requirements with Codelinks
:labels: no codelink, with codelink
:colors: red, green

type == 'comp_req' and source_code_link == ''
type == 'comp_req' and source_code_link != ''
type == 'comp_req' and is_external == False and source_code_link == ''
type == 'comp_req' and is_external == False and source_code_link != ''

.. grid-item-card::

Expand Down
20 changes: 20 additions & 0 deletions score/datarouter/test/ut/ut_logging/test_dltchannel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@ class DltChannelTest : public ::testing::Test

TEST_F(DltChannelTest, WhenCreatedDefault)
{
RecordProperty("PartiallyVerifies", "comp_req__data_router__dlt_multiple_channels");
RecordProperty("Description", "Check that a DLT log channel can be constructed with default parameters.");
RecordProperty("TestType", "requirements-based");
RecordProperty("DerivationTechnique", "requirements-analysis");

testing::StrictMock<UdpStreamOutput::Tester> outputs;
UdpStreamOutput::Tester::Instance() = &outputs;
EXPECT_CALL(outputs, construct(_, nullptr, 3490U, Eq(std::string("")))).Times(1);
Expand Down Expand Up @@ -133,6 +138,11 @@ TEST_F(DltChannelTest, WhenSendingNonverboseTwice)

TEST_F(DltChannelTest, WhenSendingVerboseTwice)
{
RecordProperty("PartiallyVerifies", "comp_req__data_router__dlt_verbose_messages");
RecordProperty("Description", "Check that two verbose DLT messages sent in a row are correctly buffered and flushed.");
RecordProperty("TestType", "requirements-based");
RecordProperty("DerivationTechnique", "requirements-analysis");

testing::StrictMock<UdpStreamOutput::Tester> outputs;
UdpStreamOutput::Tester::Instance() = &outputs;
mmsghdr mmsghdr_data;
Expand Down Expand Up @@ -445,6 +455,11 @@ TEST_F(DltChannelTest, WhenSendingFTVerboseHitsSleepCondition)

TEST_F(DltChannelTest, WhenLogLevelExceedsThreshold_Verbose)
{
RecordProperty("PartiallyVerifies", "comp_req__data_router__dlt_message_filtering");
RecordProperty("Description", "Check that a verbose message whose log level exceeds the channel threshold is filtered out.");
RecordProperty("TestType", "requirements-based");
RecordProperty("DerivationTechnique", "boundary-values");

testing::StrictMock<UdpStreamOutput::Tester> outputs;
UdpStreamOutput::Tester::Instance() = &outputs;

Expand All @@ -466,6 +481,11 @@ TEST_F(DltChannelTest, WhenLogLevelExceedsThreshold_Verbose)

TEST_F(DltChannelTest, WhenNonVerboseLogLevelExceedsThreshold)
{
RecordProperty("PartiallyVerifies", "comp_req__data_router__dlt_message_filtering");
RecordProperty("Description", "Check that a non-verbose message whose log level exceeds the channel threshold is filtered out.");
RecordProperty("TestType", "requirements-based");
RecordProperty("DerivationTechnique", "boundary-values");

testing::StrictMock<UdpStreamOutput::Tester> outputs;
UdpStreamOutput::Tester::Instance() = &outputs;

Expand Down
10 changes: 10 additions & 0 deletions score/datarouter/test/ut/ut_logging/test_dltserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,11 @@ class DltServerCreatedWithoutConfigFixture : public ::testing::Test

TEST_F(DltServerCreatedWithoutConfigFixture, WhenCreatedDefault)
{
RecordProperty("PartiallyVerifies", "comp_req__data_router__dlt_server");
RecordProperty("Description", "Check that the DLT server component can be constructed with a default config.");
RecordProperty("TestType", "requirements-based");
RecordProperty("DerivationTechnique", "requirements-analysis");

DltLogServer dlt_server(s_config, read_callback.AsStdFunction(), write_callback.AsStdFunction(), true);
}

Expand Down Expand Up @@ -234,6 +239,11 @@ TEST_F(DltServerCreatedWithConfigFixture, FlushChannelsExpectNoThrowException)

TEST_F(DltServerCreatedWithConfigFixture, GetQuotaCorrectAppNameExpectCorrectValue)
{
RecordProperty("PartiallyVerifies", "comp_req__data_router__dlt_bw_quota_config");
RecordProperty("Description", "Check that the DLT bandwidth quota configured for an application is returned correctly.");
RecordProperty("TestType", "requirements-based");
RecordProperty("DerivationTechnique", "requirements-analysis");

EXPECT_CALL(read_callback, Call()).Times(1).WillOnce(Return(p_config));
EXPECT_CALL(write_callback, Call(_)).Times(0);

Expand Down
5 changes: 5 additions & 0 deletions score/datarouter/test/ut/ut_logging/test_verbose_dlt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ class MockDltVerboseHandlerOutput : public DltVerboseHandler::IOutput

TEST(DltVerboseHandlerTest, sendVerboseTest)
{
RecordProperty("PartiallyVerifies", "comp_req__data_router__dlt_verbose_messages");
RecordProperty("Description", "Check that a handled message is forwarded to the output as a verbose DLT message.");
RecordProperty("TestType", "requirements-based");
RecordProperty("DerivationTechnique", "requirements-analysis");

MockDltVerboseHandlerOutput mock_dlt_output;
ON_CALL(mock_dlt_output, IsOutputEnabled()).WillByDefault(Return(true));
DltVerboseHandler handler(mock_dlt_output);
Expand Down
7 changes: 4 additions & 3 deletions score/mw/log/backend/slog_registrant_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ namespace

TEST(SlogRegistrantTest, SlogBackendIsRegisteredAfterStaticInitialization)
{
RecordProperty("PartiallyVerifies", "comp_req__log__system_backend_activation");
RecordProperty("Description",
"The slog backend registrant shall register a creator for LogMode::kSystem during static init.");
RecordProperty("TestType", "Verification of the control flow and data flow");
RecordProperty("DerivationTechnique", "Analysis of functional dependencies");
"The slog backend registrant is registered for LogMode::kSystem during static init.");
RecordProperty("TestType", "requirements-based");
RecordProperty("DerivationTechnique", "requirements-analysis");

EXPECT_TRUE(IsBackendAvailable(LogMode::kSystem));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -331,11 +331,14 @@ INSTANTIATE_TEST_SUITE_P(LogLevelSetCorrectly,

TEST_F(DataRouterBackendFixture, LogLevelVerbose)
{
RecordProperty("PartiallyVerifies", "comp_req__log__dlt_verbose_mode");
RecordProperty("Description", "Check that a message logged at verbose level is sent in DLT verbose mode.");
RecordProperty("TestType", "requirements-based");
RecordProperty("DerivationTechnique", "requirements-analysis");
RecordProperty("ParentRequirement", "SCR-1633144");
RecordProperty("ASIL", "B");
RecordProperty("Description", "Verify the ability of logging verbose message.");
RecordProperty("TestingTechnique", "Requirements-based test");
RecordProperty("DerivationTechnique", "Analysis of requirements");

SimulateLogging(LogLevel::kVerbose);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,14 @@ class RecorderFactoryConfigFixture : public ::testing::Test

TEST_F(RecorderFactoryConfigFixture, RemoteConfiguredShallReturnDataRouterRecorder)
{
RecordProperty("PartiallyVerifies", "comp_req__log__local_allocation_strategy");
RecordProperty("Description", "Check that the recorder factory creates a DataRouterRecorder using the provided memory resource when remote mode is configured.");
RecordProperty("TestType", "requirements-based");
RecordProperty("DerivationTechnique", "requirements-analysis");
RecordProperty("Requirement", "SCR-861534");
RecordProperty("ASIL", "B");
RecordProperty("Description", "RecorderFactory can create DataRouterRecorder if remote is configured.");
RecordProperty("TestingTechnique", "Requirements-based test");
RecordProperty("DerivationTechnique", "Analysis of requirements");

const Configuration config{};
auto recorder =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,14 @@ TEST(CommonTests, TypesShallBeTriviallyCopyable)

TEST(CommonTests, TypesShallBeLockFree)
{
RecordProperty("PartiallyVerifies", "comp_req__log__avoid_locks");
RecordProperty("Description", "Check that the atomic fields of the shared-memory data structure are lock-free.");
RecordProperty("TestType", "requirements-based");
RecordProperty("DerivationTechnique", "requirements-analysis");
RecordProperty("ParentRequirement", "SCR-861578");
RecordProperty("ASIL", "B");
RecordProperty("Description", "Checks data lock-free.");
RecordProperty("TestingTechnique", "Requirements-based test");
RecordProperty("DerivationTechnique", "Analysis of requirements");

score::mw::log::detail::SharedData data{};
ASSERT_TRUE(data.number_of_drops_buffer_full.is_lock_free());
Expand Down
Loading
Loading