Skip to content
Draft
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
54 changes: 54 additions & 0 deletions score/mw/com/test/feat_com_communication/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# *******************************************************************************
# 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
# *******************************************************************************

load("@rules_cc//cc:defs.bzl", "cc_binary")
load("@score_baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES")
load("//score/mw/com/test:pkg_application.bzl", "pkg_application")

exports_files(
["logging.json"],
visibility = ["//score/mw/com/test:__subpackages__"],
)

cc_binary(
name = "feat_com_communication_app",
srcs = [
"feat_com_communication_app.cpp",
],
data = ["mw_com_config.json"],
features = COMPILER_WARNING_FEATURES + [
"aborts_upon_exception",
],
deps = [
"//score/mw/com",
"//score/mw/com/test/common_test_resources:assert_handler",
"//score/mw/com/test/common_test_resources:sample_sender_receiver",
"//score/mw/com/test/common_test_resources:sctf_test_runner",
],
)

pkg_application(
name = "feat_com_communication-pkg",
app_name = "feat_com_communication",
bin = [":feat_com_communication_app"],
etc = [
"logging.json",
"mw_com_config.json",
"mw_com_config_recv1.json",
"mw_com_config_recv2.json",
"mw_com_config_recv3.json",
],
visibility = [
"//score/mw/com/test/feat_com_communication:__subpackages__",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*******************************************************************************
* 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/instance_specifier.h"
#include "score/mw/com/test/common_test_resources/assert_handler.h"
#include "score/mw/com/test/common_test_resources/sample_sender_receiver.h"
#include "score/mw/com/test/common_test_resources/sctf_test_runner.h"

#include <cstdlib>
#include <iostream>
#include <vector>

using namespace std::chrono_literals;

int main(int argc, const char** argv)
{
score::mw::com::test::SetupAssertHandler();
using Parameters = score::mw::com::test::SctfTestRunner::RunParameters::Parameters;

const std::vector<Parameters> allowed_parameters{
Parameters::MODE, Parameters::NUM_CYCLES, Parameters::CYCLE_TIME, Parameters::SERVICE_INSTANCE_MANIFEST, Parameters::UID};
score::mw::com::test::SctfTestRunner test_runner(argc, argv, allowed_parameters);

const auto& run_parameters = test_runner.GetRunParameters();
const auto mode = run_parameters.GetMode();
const auto num_cycles = run_parameters.GetNumCycles();
const auto stop_token = test_runner.GetStopToken();

score::mw::com::test::EventSenderReceiver event_sender_receiver{};

const auto instance_specifier_result =
score::mw::com::InstanceSpecifier::Create(std::string{"score/cp60/MapApiLanesStamped"});
if (!instance_specifier_result.has_value())
{
std::cerr << "Invalid instance specifier, terminating." << std::endl;
return EXIT_FAILURE;
}
const auto& instance_specifier = instance_specifier_result.value();

std::cout << "Starting application in mode: " << mode << std::endl;

if (mode == "send" || mode == "skeleton")
{
const auto cycle_time = run_parameters.GetCycleTime();
return event_sender_receiver.RunAsSkeleton(instance_specifier, cycle_time, num_cycles, stop_token);
}
else if (mode == "recv" || mode == "proxy")
{
const auto cycle_time = run_parameters.GetOptionalCycleTime();
return event_sender_receiver.RunAsProxy(instance_specifier, cycle_time, num_cycles, stop_token);
}
else
{
std::cerr << "Unknown mode " << mode << ", terminating." << std::endl;
return EXIT_FAILURE;
}
}
32 changes: 32 additions & 0 deletions score/mw/com/test/feat_com_communication/integration_test/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# *******************************************************************************
# 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
# *******************************************************************************
load("@rules_pkg//pkg:tar.bzl", "pkg_tar")
load("//quality/integration_testing:integration_testing.bzl", "integration_test")

pkg_tar(
name = "filesystem",
deps = [
"//score/mw/com/test/feat_com_communication:feat_com_communication-pkg",
],
)

integration_test(
name = "test_feat_com_communication",
timeout = "moderate",
srcs = [
"test_feat_com_communication.py",
"test_recovery.py",

],
filesystem = ":filesystem",
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# *******************************************************************************
# 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
# *******************************************************************************

"""Integration test to verify primary feat__com_communication communication framework robustness."""

import time

def test_feat_com_communication(sut):
"""Verifies that the proxy receives IPC events from the skeleton concurrently over standard configurations."""
with sut.start_process("./bin/feat_com_communication_app --mode send -t 50 -n 20", cwd="/opt/feat_com_communication/") as sender_process:
with sut.start_process("./bin/feat_com_communication_app --mode recv -n 15", cwd="/opt/feat_com_communication/") as receiver_process:
assert receiver_process.wait_for_exit(timeout=60) == 0

assert sender_process.wait_for_exit(timeout=60) == 0

def test_feat_com_broadcasting_multiple_receivers(sut):
"""Verifies that multiple proxies can concurrently receive data from a single skeleton."""
with sut.start_process("./bin/feat_com_communication_app --mode send -t 50 -n 400", cwd="/opt/feat_com_communication/") as sender_process:

with sut.start_process("./bin/feat_com_communication_app --mode recv -n 15 --service_instance_manifest etc/mw_com_config_recv1.json", cwd="/opt/feat_com_communication/") as recv1:
time.sleep(1.0)
with sut.start_process("./bin/feat_com_communication_app --mode recv -n 15 --service_instance_manifest etc/mw_com_config_recv2.json", cwd="/opt/feat_com_communication/") as recv2:
time.sleep(1.0)
with sut.start_process("./bin/feat_com_communication_app --mode recv -n 15 --service_instance_manifest etc/mw_com_config_recv3.json", cwd="/opt/feat_com_communication/") as recv3:


assert recv1.wait_for_exit(timeout=90) == 0
assert recv2.wait_for_exit(timeout=90) == 0
assert recv3.wait_for_exit(timeout=90) == 0

assert sender_process.wait_for_exit(timeout=90) == 0

def test_feat_com_late_subscriber_joins(sut):
"""Verifies that a proxy can successfully discover and receive data after the skeleton has already started publishing."""
with sut.start_process("./bin/feat_com_communication_app --mode send -t 50 -n 40", cwd="/opt/feat_com_communication/") as sender_process:

time.sleep(1.0)
with sut.start_process("./bin/feat_com_communication_app --mode recv -n 15", cwd="/opt/feat_com_communication/") as receiver_process:
assert receiver_process.wait_for_exit(timeout=60) == 0

assert sender_process.wait_for_exit(timeout=60) == 0
Original file line number Diff line number Diff line change
@@ -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
# *******************************************************************************

"""Integration test to verify Skeleton crash recovery and system integrity."""

import time
import pytest

def test_skeleton_restart_integrity(sut):
"""Verifies that a new skeleton can be started and discovered after a previous one was killed."""

app_bin = "./bin/feat_com_communication_app"
app_cwd = "/opt/feat_com_communication/"

# 1. Start the first Skeleton
print("Starting Skeleton A...")
with sut.start_process(f"{app_bin} --mode send -t 50 -n 200", cwd=app_cwd) as skeleton_a:
time.sleep(3.0)

print("Starting Proxy A...")
with sut.start_process(f"{app_bin} --mode recv -n 5", cwd=app_cwd) as proxy_a:
assert proxy_a.wait_for_exit(timeout=30) == 0
print("Proxy A finished successfully.")

print("Force-killing Skeleton A (SIGKILL)...")
sut.execute("pkill -9 -f mode\\ send")

for _ in range(5):
exit_code, output = sut.execute("pgrep -f mode\\ send")
if exit_code != 0:
print("Confirmed: Skeleton A process is gone.")
break
time.sleep(1.0)
else:
print(f"Warning: Skeleton A might still be running! pgrep output: {output}")

print("Waiting for system to settle and cleaning artifacts...")
time.sleep(5.0)
sut.execute("rm -rf /tmp/mw_com_lola")

print("Starting Skeleton B (Recovery instance)...")
with sut.start_process(f"{app_bin} --mode send -t 50 -n 100", cwd=app_cwd) as skeleton_b:
time.sleep(3.0)
print("Starting Proxy B...")
with sut.start_process(f"{app_bin} --mode recv -n 5", cwd=app_cwd) as proxy_b:
assert proxy_b.wait_for_exit(timeout=30) == 0
print("Proxy B finished successfully. System recovered.")

assert skeleton_b.wait_for_exit(timeout=60) == 0
14 changes: 14 additions & 0 deletions score/mw/com/test/feat_com_communication/logging.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"appId": "feat_com",
"appDesc": "communication test",
"logLevel": "kDebug",
"logLevelThresholdConsole": "kDebug",
"logMode": "kConsole",
"dynamicDatarouterIdentifiers": true,
"service_types": {
"score/cp60/MapApiLanesStamped": {
"instance_id": "default",
"binding": "ipc"
}
}
}
59 changes: 59 additions & 0 deletions score/mw/com/test/feat_com_communication/mw_com_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{
"serviceTypes": [
{
"serviceTypeName": "BigDataService",
"version": {
"major": 1,
"minor": 0
},
"bindings": [
{
"binding": "SHM",
"serviceId": 99,
"events": [
{
"eventName": "map_api_lanes_stamped",
"eventId": 1
},
{
"eventName": "dummy_data_stamped",
"eventId": 2
}
]
}
]
}
],
"serviceInstances": [
{
"instanceSpecifier": "score/cp60/MapApiLanesStamped",
"serviceTypeName": "BigDataService",
"version": {
"major": 1,
"minor": 0
},
"instances": [
{
"instanceId": 1,
"asil-level": "QM",
"binding": "SHM",
"events": [
{
"eventName": "map_api_lanes_stamped",
"maxSubscribers": 10,
"numberOfSampleSlots": 15
},
{
"eventName": "dummy_data_stamped",
"maxSubscribers": 10,
"numberOfSampleSlots": 15
}
]
}
]
}
],
"global": {
"asil-level": "QM"
}
}
Loading