Skip to content

Commit fe2db0a

Browse files
mw/com: skeleton field slot count optional when WithNotifier is disabled
1 parent 5e6e75a commit fe2db0a

4 files changed

Lines changed: 251 additions & 5 deletions

File tree

score/mw/com/impl/plumbing/skeleton_service_element_binding_factory_impl.h

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,13 @@ namespace detail
4646

4747
template <typename LolaServiceElementInstanceDeployment>
4848
lola::SkeletonEventProperties GetSkeletonEventProperties(
49-
const LolaServiceElementInstanceDeployment& lola_service_element_instance_deployment)
49+
const LolaServiceElementInstanceDeployment& lola_service_element_instance_deployment,
50+
std::optional<std::uint16_t> slot_count_override = std::nullopt)
5051
{
51-
if (!lola_service_element_instance_deployment.GetNumberOfSampleSlots().has_value())
52+
const auto effective_slot_count = slot_count_override.has_value()
53+
? slot_count_override
54+
: lola_service_element_instance_deployment.GetNumberOfSampleSlots();
55+
if (!effective_slot_count.has_value())
5256
{
5357
score::mw::log::LogFatal("lola")
5458
<< "Could not create SkeletonEventProperties from ServiceElementInstanceDeployment. Number of sample slots "
@@ -63,7 +67,7 @@ lola::SkeletonEventProperties GetSkeletonEventProperties(
6367
"not specified in the configuration. Terminating.";
6468
std::terminate();
6569
}
66-
return lola::SkeletonEventProperties{lola_service_element_instance_deployment.GetNumberOfSampleSlots().value(),
70+
return lola::SkeletonEventProperties{effective_slot_count.value(),
6771
lola_service_element_instance_deployment.max_subscribers_.value(),
6872
lola_service_element_instance_deployment.enforce_max_samples_};
6973
}
@@ -107,8 +111,15 @@ auto CreateSkeletonServiceElement(const InstanceIdentifier& identifier,
107111
const std::string service_element_name_str{service_element_name};
108112
const auto& lola_service_element_instance_deployment = GetServiceElementInstanceDeployment<element_type>(
109113
lola_service_instance_deployment, service_element_name_str);
114+
115+
// Only fields use the slot-count override (set by SkeletonField when WithNotifier is disabled).
116+
std::optional<std::uint16_t> slot_count_override{std::nullopt};
117+
if constexpr (element_type == ServiceElementType::FIELD)
118+
{
119+
slot_count_override = SkeletonBaseView{parent}.GetSlotCountOverride();
120+
}
110121
const auto skeleton_event_properties =
111-
detail::GetSkeletonEventProperties(lola_service_element_instance_deployment);
122+
detail::GetSkeletonEventProperties(lola_service_element_instance_deployment, slot_count_override);
112123

113124
const auto lola_service_element_id =
114125
GetServiceElementId<element_type>(lola_service_type_deployment, service_element_name_str);

score/mw/com/impl/skeleton_base.h

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include <functional>
3434
#include <map>
3535
#include <memory>
36+
#include <optional>
3637
#include <string_view>
3738

3839
namespace score::mw::com::impl
@@ -108,6 +109,10 @@ class SkeletonBase
108109

109110
ISkeletonBase* skeleton_mock_;
110111

112+
/// Slot-count override consulted by the binding factory during field construction. Managed by
113+
/// SlotCountOverrideGuard.
114+
std::optional<std::uint16_t> current_slot_count_override_{std::nullopt};
115+
111116
[[nodiscard]] score::Result<void> OfferServiceEvents() const noexcept;
112117
[[nodiscard]] score::Result<void> OfferServiceFields() const noexcept;
113118

@@ -129,6 +134,15 @@ class SkeletonBaseView
129134
return skeleton_base_.binding_.get();
130135
}
131136

137+
void SetSlotCountOverride(std::optional<std::uint16_t> slot_count) noexcept
138+
{
139+
skeleton_base_.current_slot_count_override_ = slot_count;
140+
}
141+
std::optional<std::uint16_t> GetSlotCountOverride() const noexcept
142+
{
143+
return skeleton_base_.current_slot_count_override_;
144+
}
145+
132146
void RegisterEvent(const std::string_view event_name, SkeletonEventBase& event)
133147
{
134148
const auto result = skeleton_base_.events_.emplace(event_name, event);
@@ -217,7 +231,28 @@ class SkeletonBaseView
217231
SkeletonBase& skeleton_base_;
218232
};
219233

220-
score::cpp::optional<InstanceIdentifier> GetInstanceIdentifier(const InstanceSpecifier& specifier);
234+
/// RAII based slot counter update.
235+
class SlotCountOverrideGuard
236+
{
237+
public:
238+
SlotCountOverrideGuard(SkeletonBase& parent, std::uint16_t slot_count) noexcept : parent_{parent}
239+
{
240+
SkeletonBaseView{parent_}.SetSlotCountOverride(slot_count);
241+
}
242+
~SlotCountOverrideGuard() noexcept
243+
{
244+
SkeletonBaseView{parent_}.SetSlotCountOverride(std::nullopt);
245+
}
246+
247+
SlotCountOverrideGuard(const SlotCountOverrideGuard&) = delete;
248+
SlotCountOverrideGuard& operator=(const SlotCountOverrideGuard&) = delete;
249+
SlotCountOverrideGuard(SlotCountOverrideGuard&&) = delete;
250+
SlotCountOverrideGuard& operator=(SlotCountOverrideGuard&&) = delete;
251+
252+
private:
253+
SkeletonBase& parent_;
254+
255+
score::cpp::optional<InstanceIdentifier> GetInstanceIdentifier(const InstanceSpecifier& specifier);
221256

222257
} // namespace score::mw::com::impl
223258

score/mw/com/impl/skeleton_field.h

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@
1313
#ifndef SCORE_MW_COM_IMPL_SKELETON_FIELD_H
1414
#define SCORE_MW_COM_IMPL_SKELETON_FIELD_H
1515

16+
#include "score/mw/com/impl/configuration/lola_service_instance_deployment.h"
17+
#include "score/mw/com/impl/configuration/someip_service_instance_deployment.h"
1618
#include "score/mw/com/impl/field_tags.h"
19+
#include "score/mw/com/impl/instance_identifier.h"
1720
#include "score/mw/com/impl/method_type.h"
1821
#include "score/mw/com/impl/methods/skeleton_method.h"
1922
#include "score/mw/com/impl/plumbing/sample_allocatee_ptr.h"
@@ -27,12 +30,17 @@
2730
#include "score/result/result.h"
2831

2932
#include <score/assert.hpp>
33+
#include <score/blank.hpp>
3034
#include <score/callback.hpp>
35+
#include <score/overload.hpp>
3136

37+
#include <cstdint>
3238
#include <memory>
39+
#include <string>
3340
#include <string_view>
3441
#include <type_traits>
3542
#include <utility>
43+
#include <variant>
3644

3745
namespace score::mw::com::impl
3846
{
@@ -169,9 +177,46 @@ class SkeletonField : public SkeletonFieldBase
169177
return true;
170178
}
171179

180+
static constexpr bool kHasNotifier = detail::contains_type<WithNotifier, Tags...>::value;
181+
static constexpr std::uint16_t kDefaultSlotCountWithoutNotifier{2U};
182+
183+
static void WarnIfSlotCountConfiguredWithoutNotifier(SkeletonBase& parent, const std::string_view field_name)
184+
{
185+
const auto identifier = SkeletonBaseView{parent}.GetAssociatedInstanceIdentifier();
186+
const auto& deployment = InstanceIdentifierView{identifier}.GetServiceInstanceDeployment();
187+
auto visitor = score::cpp::overload(
188+
[field_name](const LolaServiceInstanceDeployment& lola) noexcept {
189+
const auto it = lola.fields_.find(std::string{field_name});
190+
if (it == lola.fields_.cend())
191+
{
192+
score::mw::log::LogError("lola") << "Field '" << field_name
193+
<< " is missing from LolaServiceInstanceDeployment; slot count "
194+
"configuration cannot be applied.";
195+
return;
196+
}
197+
if (it->second.GetNumberOfSampleSlots().has_value())
198+
{
199+
score::mw::log::LogWarn("lola")
200+
<< "Field '" << field_name
201+
<< "' has WithNotifier disabled; configured numberOfSampleSlots is ignored.";
202+
}
203+
},
204+
[](const SomeIpServiceInstanceDeployment&) noexcept {},
205+
// coverity[autosar_cpp14_a7_1_7_violation]
206+
[](const score::cpp::blank&) noexcept {});
207+
std::visit(visitor, deployment.bindingInfo_);
208+
}
209+
172210
static std::unique_ptr<SkeletonEvent<FieldType>> MakeSkeletonEvent(SkeletonBase& parent,
173211
const std::string_view field_name)
174212
{
213+
// WithNotifier disabled: it was decided to use 2 slots and warn if user tried to set a different value.
214+
std::optional<SlotCountOverrideGuard> slot_count_guard;
215+
if constexpr (!kHasNotifier)
216+
{
217+
WarnIfSlotCountConfiguredWithoutNotifier(parent, field_name);
218+
slot_count_guard.emplace(parent, kDefaultSlotCountWithoutNotifier);
219+
}
175220
return std::make_unique<SkeletonEvent<FieldType>>(
176221
parent,
177222
field_name,

score/mw/com/impl/skeleton_field_test.cpp

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@
2424
#include <gmock/gmock.h>
2525
#include <gtest/gtest.h>
2626
#include <cstdint>
27+
#include <list>
2728
#include <memory>
29+
#include <optional>
30+
#include <string>
2831
#include <string_view>
2932
#include <type_traits>
3033
#include <utility>
@@ -1558,5 +1561,157 @@ TEST(SkeletonFieldSetHandlerTest, SecondRegisterSetHandlerReplacesHandler)
15581561
EXPECT_TRUE(second_callback_called);
15591562
}
15601563

1564+
// ──────────────────────────────────────────────────────────────────────────────
1565+
// SkeletonField slot-count normalization when WithNotifier is enabled / disabled.
1566+
//
1567+
// Behaviour matrix:
1568+
// WithNotifier | slot count in config | outcome
1569+
// ------------- | -------------------- | --------------------------------------------------
1570+
// enabled | configured | use the provided slot count.
1571+
// enabled | missing | factory terminates.
1572+
// disabled | configured | ignore the user set config and log warning, force to 2.
1573+
// disabled | missing | silently set to 2
1574+
1575+
class MyNotifierSkeleton : public SkeletonBase
1576+
{
1577+
public:
1578+
using SkeletonBase::SkeletonBase;
1579+
SkeletonField<TestSampleType, WithGetter, WithNotifier> field_{*this, kFieldName};
1580+
};
1581+
1582+
class MyGetterOnlySkeleton : public SkeletonBase
1583+
{
1584+
public:
1585+
using SkeletonBase::SkeletonBase;
1586+
SkeletonField<TestSampleType, WithGetter> field_{*this, kFieldName};
1587+
};
1588+
1589+
namespace
1590+
{
1591+
1592+
InstanceIdentifier MakeInstanceIdentifierWithFieldSlotCount(std::optional<std::uint16_t> slot_count)
1593+
{
1594+
LolaFieldInstanceDeployment field_deployment{slot_count,
1595+
std::optional<LolaFieldInstanceDeployment::SubscriberCountType>{1U},
1596+
std::optional<std::uint8_t>{},
1597+
false,
1598+
LolaFieldInstanceDeployment::TracingSlotSizeType{0U}};
1599+
LolaServiceInstanceDeployment lola_deployment{LolaServiceInstanceId{kInstanceId}};
1600+
lola_deployment.fields_.emplace(std::string{kFieldName}, field_deployment);
1601+
// std::list is used (not std::vector) because the constructed InstanceIdentifier holds a pointer back into
1602+
// the ServiceInstanceDeployment we push here. List preserves references when more elements are added; vector
1603+
// does not (reallocation invalidates prior references).
1604+
static std::list<ServiceInstanceDeployment> kept_deployments;
1605+
kept_deployments.emplace_back(
1606+
kServiceIdentifier, std::move(lola_deployment), QualityType::kASIL_QM, kInstanceSpecifier);
1607+
return make_InstanceIdentifier(kept_deployments.back(), kTypeDeployment);
1608+
}
1609+
1610+
std::optional<std::uint16_t> GetFieldSlotCount(const InstanceIdentifier& identifier)
1611+
{
1612+
const InstanceIdentifierView identifier_view{identifier};
1613+
const auto& service_instance_deployment = identifier_view.GetServiceInstanceDeployment();
1614+
const auto& lola_deployment =
1615+
GetServiceInstanceDeploymentBinding<LolaServiceInstanceDeployment>(service_instance_deployment);
1616+
const auto field_iter = lola_deployment.fields_.find(std::string{kFieldName});
1617+
SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(field_iter != lola_deployment.fields_.cend());
1618+
return field_iter->second.GetNumberOfSampleSlots();
1619+
}
1620+
1621+
} // namespace
1622+
1623+
// The sidechannel: SkeletonField writes the override on the parent SkeletonBase before invoking the binding
1624+
// factory and clears it after. These tests use a lambda on the mocked CreateEventBinding to capture the override
1625+
// at the moment the factory sees it, then assert (a) the captured value matches the expected behaviour for the
1626+
// (notifier-tag, configured-slot-count) cell, and (b) the override is cleared again once construction returns.
1627+
TEST(SkeletonFieldSlotCountTest, NotifierEnabledWithConfiguredSlotCountLeavesOverrideUnset)
1628+
{
1629+
RuntimeMockGuard runtime_mock_guard{};
1630+
ON_CALL(runtime_mock_guard.runtime_mock_, GetTracingFilterConfig()).WillByDefault(Return(nullptr));
1631+
SkeletonFieldBindingFactoryMockGuard<TestSampleType> skeleton_field_binding_factory_mock_guard{};
1632+
1633+
std::optional<std::uint16_t> override_at_factory_call{};
1634+
EXPECT_CALL(skeleton_field_binding_factory_mock_guard.factory_mock_, CreateEventBinding(_, _, kFieldName))
1635+
.WillOnce(Invoke([&](const InstanceIdentifier&, SkeletonBase& parent, const std::string_view) {
1636+
override_at_factory_call = SkeletonBaseView{parent}.GetSlotCountOverride();
1637+
return std::make_unique<mock_binding::SkeletonEvent<TestSampleType>>();
1638+
}));
1639+
1640+
const auto identifier = MakeInstanceIdentifierWithFieldSlotCount(std::uint16_t{5U});
1641+
1642+
MyNotifierSkeleton skeleton{std::make_unique<mock_binding::Skeleton>(), identifier};
1643+
1644+
EXPECT_FALSE(override_at_factory_call.has_value());
1645+
EXPECT_FALSE(SkeletonBaseView{skeleton}.GetSlotCountOverride().has_value());
1646+
EXPECT_EQ(GetFieldSlotCount(identifier), std::uint16_t{5U});
1647+
}
1648+
1649+
TEST(SkeletonFieldSlotCountTest, NotifierEnabledWithMissingSlotCountLeavesOverrideUnset)
1650+
{
1651+
RuntimeMockGuard runtime_mock_guard{};
1652+
ON_CALL(runtime_mock_guard.runtime_mock_, GetTracingFilterConfig()).WillByDefault(Return(nullptr));
1653+
SkeletonFieldBindingFactoryMockGuard<TestSampleType> skeleton_field_binding_factory_mock_guard{};
1654+
1655+
std::optional<std::uint16_t> override_at_factory_call{};
1656+
EXPECT_CALL(skeleton_field_binding_factory_mock_guard.factory_mock_, CreateEventBinding(_, _, kFieldName))
1657+
.WillOnce(Invoke([&](const InstanceIdentifier&, SkeletonBase& parent, const std::string_view) {
1658+
override_at_factory_call = SkeletonBaseView{parent}.GetSlotCountOverride();
1659+
return std::make_unique<mock_binding::SkeletonEvent<TestSampleType>>();
1660+
}));
1661+
1662+
const auto identifier = MakeInstanceIdentifierWithFieldSlotCount(std::nullopt);
1663+
1664+
MyNotifierSkeleton skeleton{std::make_unique<mock_binding::Skeleton>(), identifier};
1665+
1666+
EXPECT_FALSE(override_at_factory_call.has_value());
1667+
EXPECT_FALSE(SkeletonBaseView{skeleton}.GetSlotCountOverride().has_value());
1668+
EXPECT_FALSE(GetFieldSlotCount(identifier).has_value());
1669+
}
1670+
1671+
TEST(SkeletonFieldSlotCountTest, NotifierDisabledWithConfiguredSlotCountSetsOverrideToDefault)
1672+
{
1673+
RuntimeMockGuard runtime_mock_guard{};
1674+
ON_CALL(runtime_mock_guard.runtime_mock_, GetTracingFilterConfig()).WillByDefault(Return(nullptr));
1675+
SkeletonFieldBindingFactoryMockGuard<TestSampleType> skeleton_field_binding_factory_mock_guard{};
1676+
1677+
std::optional<std::uint16_t> override_at_factory_call{};
1678+
EXPECT_CALL(skeleton_field_binding_factory_mock_guard.factory_mock_, CreateEventBinding(_, _, kFieldName))
1679+
.WillOnce(Invoke([&](const InstanceIdentifier&, SkeletonBase& parent, const std::string_view) {
1680+
override_at_factory_call = SkeletonBaseView{parent}.GetSlotCountOverride();
1681+
return std::make_unique<mock_binding::SkeletonEvent<TestSampleType>>();
1682+
}));
1683+
1684+
const auto identifier = MakeInstanceIdentifierWithFieldSlotCount(std::uint16_t{5U});
1685+
1686+
MyGetterOnlySkeleton skeleton{std::make_unique<mock_binding::Skeleton>(), identifier};
1687+
1688+
EXPECT_EQ(override_at_factory_call, std::uint16_t{2U});
1689+
EXPECT_FALSE(SkeletonBaseView{skeleton}.GetSlotCountOverride().has_value());
1690+
// Deployment itself is not mutated by the sidechannel — only the per-skeleton override slot is touched.
1691+
EXPECT_EQ(GetFieldSlotCount(identifier), std::uint16_t{5U});
1692+
}
1693+
1694+
TEST(SkeletonFieldSlotCountTest, NotifierDisabledWithMissingSlotCountSetsOverrideToDefault)
1695+
{
1696+
RuntimeMockGuard runtime_mock_guard{};
1697+
ON_CALL(runtime_mock_guard.runtime_mock_, GetTracingFilterConfig()).WillByDefault(Return(nullptr));
1698+
SkeletonFieldBindingFactoryMockGuard<TestSampleType> skeleton_field_binding_factory_mock_guard{};
1699+
1700+
std::optional<std::uint16_t> override_at_factory_call{};
1701+
EXPECT_CALL(skeleton_field_binding_factory_mock_guard.factory_mock_, CreateEventBinding(_, _, kFieldName))
1702+
.WillOnce(Invoke([&](const InstanceIdentifier&, SkeletonBase& parent, const std::string_view) {
1703+
override_at_factory_call = SkeletonBaseView{parent}.GetSlotCountOverride();
1704+
return std::make_unique<mock_binding::SkeletonEvent<TestSampleType>>();
1705+
}));
1706+
1707+
const auto identifier = MakeInstanceIdentifierWithFieldSlotCount(std::nullopt);
1708+
1709+
MyGetterOnlySkeleton skeleton{std::make_unique<mock_binding::Skeleton>(), identifier};
1710+
1711+
EXPECT_EQ(override_at_factory_call, std::uint16_t{2U});
1712+
EXPECT_FALSE(SkeletonBaseView{skeleton}.GetSlotCountOverride().has_value());
1713+
EXPECT_FALSE(GetFieldSlotCount(identifier).has_value());
1714+
}
1715+
15611716
} // namespace
15621717
} // namespace score::mw::com::impl

0 commit comments

Comments
 (0)