Skip to content
20 changes: 20 additions & 0 deletions score/mw/com/impl/configuration/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,32 @@ cc_library(
visibility = ["//score/mw/com/impl/bindings/lola/messaging:__pkg__"],
)

cc_library(
name = "config_validate",
srcs = ["config_validate.cpp"],
hdrs = ["config_validate.h"],
features = COMPILER_WARNING_FEATURES,
tags = ["FFI"],
visibility = [
"//score/mw/com/impl/configuration:__subpackages__",
],
deps = [
":configuration_local",
":lola_service_instance_deployment",
":lola_service_type_deployment",
"//score/mw/com/impl:instance_specifier",
"@score_baselibs//score/language/futurecpp",
"@score_baselibs//score/mw/log",
],
)

cc_library(
name = "config_parser",
srcs = ["config_parser.cpp"],
hdrs = ["config_parser.h"],
features = COMPILER_WARNING_FEATURES,
implementation_deps = [
":config_validate",
":lola_service_instance_deployment",
":quality_type",
":service_type_deployment",
Expand Down
274 changes: 25 additions & 249 deletions score/mw/com/impl/configuration/config_parser.cpp

Large diffs are not rendered by default.

32 changes: 32 additions & 0 deletions score/mw/com/impl/configuration/config_parser_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,38 @@ TEST_F(ConfigParserFixture, NoEventsOrFieldsWillCauseTermination)
SCORE_LANGUAGE_FUTURECPP_EXPECT_CONTRACT_VIOLATED(score::mw::com::impl::configuration::Parse(std::move(j2)));
}

TEST_F(ConfigParserFixture, EmptyEventsFieldsMethodsListWillCauseTermination)
{
// Given a JSON where events fields methods keys are present but all lists are empty
auto j2 = R"(
{
"serviceTypes": [
{
"serviceTypeName": "/score/ncar/services/TirePressureService",
"version": {
"major": 12,
"minor": 34
},
"bindings": [
{
"binding": "SHM",
"serviceId": 1234,
"events": [],
"fields": [],
"methods": []
}
]
}
],
"serviceInstances": []
}
)"_json;

// When parsing the JSON
// That the application will terminate
SCORE_LANGUAGE_FUTURECPP_EXPECT_CONTRACT_VIOLATED(score::mw::com::impl::configuration::Parse(std::move(j2)));
Comment on lines +433 to +434

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its not a bug - its a feature :)

There have been use cases in the past, where an empty service was used. We see no reason why an empty service (e.g. as marker via the service discovery) shall not be allowed.

Can you maybe elaborate a bit, why you think this is a bug?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the original code you can see the in config_parser.cpp the error message description as following:
score::mw::log::LogFatal("lola") << "Configuration should contain at least one event, field, or method.";
That's why I considered it as an unintended bug.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we consider it as a feature then we shall remove the check at all. What do you suggest?

}

TEST_F(ConfigParserFixture, NoEventNameWillCauseTermination)
{
// Given a JSON with a missing event name
Expand Down
164 changes: 164 additions & 0 deletions score/mw/com/impl/configuration/config_validate.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/********************************************************************************
* 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/com/impl/configuration/config_validate.h"

#include "score/mw/com/impl/configuration/lola_event_id.h"
#include "score/mw/com/impl/configuration/lola_field_id.h"
#include "score/mw/com/impl/configuration/lola_method_id.h"
#include "score/mw/com/impl/configuration/lola_service_instance_deployment.h"

#include <set>
#include <type_traits>
#include <variant>

namespace score::mw::com::impl::configuration
{

void ValidateServiceTypeHasElements(const LolaServiceTypeDeployment& deployment)
{
if (deployment.events_.empty() && deployment.fields_.empty() && deployment.methods_.empty())
{
score::mw::log::LogFatal("lola") << "Configuration should contain at least one event, field, or method.";
SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(false);
}
}

void ValidateUniqueServiceElementIds(const LolaServiceTypeDeployment& deployment)
{
static_assert(std::is_same<LolaEventId, LolaFieldId>::value,
"EventId and FieldId should have the same underlying type.");
static_assert(std::is_same<LolaEventId, LolaMethodId>::value,
"EventId and MethodId should have the same underlying type.");
std::set<LolaEventId> ids{};

for (const auto& event : deployment.events_)
{
if (!ids.insert(event.second).second)
{
score::mw::log::LogFatal("lola") << "Configuration cannot contain duplicate eventId, fieldId, or methodId.";
SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(false);
}
}

for (const auto& field : deployment.fields_)
{
if (!ids.insert(field.second).second)
{
score::mw::log::LogFatal("lola") << "Configuration cannot contain duplicate eventId, fieldId, or methodId.";
SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(false);
}
}

for (const auto& method : deployment.methods_)
{
if (!ids.insert(method.second).second)
{
score::mw::log::LogFatal("lola") << "Configuration cannot contain duplicate eventId, fieldId, or methodId.";
SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(false);
}
}
}

InstanceSpecifier CreateValidInstanceSpecifier(std::string instance_specifier_name)
{
auto result = InstanceSpecifier::Create(std::move(instance_specifier_name));
if (!result.has_value())
{
score::mw::log::LogFatal("lola") << "Invalid InstanceSpecifier.";
SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(false);
}
return result.value();
}

void CrosscheckAsilLevels(const Configuration& config)
{
for (const auto& service_instance : config.GetServiceInstances())
{
if ((service_instance.second.asilLevel_ == QualityType::kASIL_B) &&
(config.GetGlobalConfiguration().GetProcessAsilLevel() != QualityType::kASIL_B))
{
::score::mw::log::LogFatal("lola")
<< "Service instance has a higher ASIL than the process. This is invalid, terminating";
SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(false);
}
}
}

// coverity[autosar_cpp14_a15_5_3_violation]
void CrosscheckServiceInstancesToTypes(const Configuration& config)
{
for (const auto& service_instance : config.GetServiceInstances())
{
const auto foundServiceType = config.GetServiceTypes().find(service_instance.second.service_);
if (foundServiceType == config.GetServiceTypes().cend())
{
::score::mw::log::LogFatal("lola")
<< "Service instance " << service_instance.first << "refers to a service type ("
<< service_instance.second.service_.ToString()
<< "), which is not configured. This is invalid, terminating";
SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(false);
}
// LCOV_EXCL_BR_START: Defensive programming: Parse() currently terminates if the ServiceInstanceDeployment
// contains anything other than a Lola binding. Therefore, it's impossible to reach this point without
// a LolaServiceInstanceDeployment.
if (!std::holds_alternative<LolaServiceInstanceDeployment>(service_instance.second.bindingInfo_))
{
// LCOV_EXCL_BR_STOP
// LCOV_EXCL_START defensive programming
::score::mw::log::LogFatal("lola")
<< "Service instance " << service_instance.first
<< "refers to an not yet supported binding. This is invalid, terminating";
SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(false);
// LCOV_EXCL_STOP
}
if (!std::holds_alternative<LolaServiceTypeDeployment>(foundServiceType->second.binding_info_))
{
::score::mw::log::LogFatal("lola")
<< "Service type " << service_instance.second.service_.ToString()
<< "refers to an not yet supported binding. This is invalid, terminating";
SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(false);
}
const auto& serviceInstanceDeployment =
std::get<LolaServiceInstanceDeployment>(service_instance.second.bindingInfo_);
for (const auto& eventInstanceElement : serviceInstanceDeployment.events_)
{
const auto& serviceTypeDeployment =
std::get<LolaServiceTypeDeployment>(foundServiceType->second.binding_info_);
const auto search = serviceTypeDeployment.events_.find(eventInstanceElement.first);
if (search == serviceTypeDeployment.events_.cend())
{
::score::mw::log::LogFatal("lola")
<< "Service instance " << service_instance.first << "event" << eventInstanceElement.first
<< "refers to an event, which doesn't exist in the referenced service type ("
<< service_instance.second.service_.ToString() << "). This is invalid, terminating";
SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(false);
}
}
for (const auto& fieldInstanceElement : serviceInstanceDeployment.fields_)
{
const auto& serviceTypeDeployment =
std::get<LolaServiceTypeDeployment>(foundServiceType->second.binding_info_);
const auto search = serviceTypeDeployment.fields_.find(fieldInstanceElement.first);
if (search == serviceTypeDeployment.fields_.cend())
{
::score::mw::log::LogFatal("lola")
<< "Service instance " << service_instance.first << "field" << fieldInstanceElement.first
<< "refers to a field, which doesn't exist in the referenced service type ("
<< service_instance.second.service_.ToString() << "). This is invalid, terminating";
SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(false);
}
}
}
}

} // namespace score::mw::com::impl::configuration
63 changes: 63 additions & 0 deletions score/mw/com/impl/configuration/config_validate.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/********************************************************************************
* 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_COM_IMPL_CONFIGURATION_CONFIG_VALIDATE_H
#define SCORE_MW_COM_IMPL_CONFIGURATION_CONFIG_VALIDATE_H

#include "score/mw/com/impl/configuration/configuration.h"
#include "score/mw/com/impl/configuration/lola_service_type_deployment.h"
#include "score/mw/com/impl/instance_specifier.h"

#include "score/mw/log/logging.h"

#include <score/assert.hpp>

#include <string>
#include <string_view>
#include <utility>
#include <vector>

namespace score::mw::com::impl::configuration
{

template <typename Map, typename Key, typename Value>
void EmplaceOrFatal(Map& map, Key&& key, Value&& value, std::string_view element_description)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is local to config_parser.cpp thus, it should be within an anonymous namespace within the config parser.

This statement is also true for all other extractions that are done in later commits.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function will mostly be also used for the flatbuffer parser, that's why I put it in the common config_validate.h

If you suggest adding these files directly to the 'configuration_json_parsing_strategy' lib directly instead of making it as a separate common lib 'config_validate' that to be used later with 'configuration_flatbuffer_parsing_strategy' I would see it okay.

Or did I missunderstood something?

{
const auto result = map.emplace(std::forward<Key>(key), std::forward<Value>(value));
if (!result.second)
{
score::mw::log::LogFatal("lola") << element_description << " was configured twice.";
SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(false);
}
}

void ValidateServiceTypeHasElements(const LolaServiceTypeDeployment& deployment);
void ValidateUniqueServiceElementIds(const LolaServiceTypeDeployment& deployment);
InstanceSpecifier CreateValidInstanceSpecifier(std::string instance_specifier_name);

template <typename Container>
void ValidateSingleDeployment(const Container& deployments, const ServiceIdentifierType& service_identifier)
{
if (deployments.size() != 1U)
{
score::mw::log::LogFatal("lola") << "More or less then one deployment for " << service_identifier.ToString()
<< ". Multi-Binding support right now not supported";
SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(false);
}
}

void CrosscheckAsilLevels(const Configuration& config);
void CrosscheckServiceInstancesToTypes(const Configuration& config);

} // namespace score::mw::com::impl::configuration

#endif // SCORE_MW_COM_IMPL_CONFIGURATION_CONFIG_VALIDATE_H
16 changes: 0 additions & 16 deletions score/mw/com/test/basic_rust_api/etc/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,6 @@
}
]
},
{
"serviceTypeName": "/score/adp/DummyServiceType",
"version": {
"major": 1,
"minor": 0
},
"bindings": [
{
"binding": "SHM",
"serviceId": 6433,
"events": [
]
}
]
},
{
"serviceTypeName": "/score/test/MixedPrimitivesInterface",
"version": {
Expand Down Expand Up @@ -187,4 +172,3 @@
}
]
}

15 changes: 0 additions & 15 deletions score/mw/com/test/bigdata/mw_com_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,6 @@
]
}
]
},
{
"serviceTypeName": "/bmw/adp/DummyServiceType",
"version": {
"major": 1,
"minor": 0
},
"bindings": [
{
"binding": "SHM",
"serviceId": 6433,
"events": [
]
}
]
}
],
"serviceInstances": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,6 @@
]
}
]
},
{
"serviceTypeName": "/bmw/adp/DummyServiceType",
"version": {
"major": 1,
"minor": 0
},
"bindings": [
{
"binding": "SHM",
"serviceId": 6433,
"events": [
]
}
]
}
],
"serviceInstances": [
Expand Down
Loading
Loading