diff --git a/score/mw/com/impl/rust/com-api/com-api-integration-lola-test/BUILD b/score/mw/com/impl/rust/com-api/com-api-integration-lola-test/BUILD new file mode 100644 index 000000000..5e51f2fc5 --- /dev/null +++ b/score/mw/com/impl/rust/com-api/com-api-integration-lola-test/BUILD @@ -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 +# ******************************************************************************* + +load("@rules_cc//cc:defs.bzl", "cc_library") +load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test") +load("//score/mw:common_features.bzl", "COMPILER_WARNING_FEATURES") + +filegroup( + name = "integration_test_config", + srcs = ["config.json"], + visibility = ["//:__subpackages__"], +) + +cc_library( + name = "integration_test_gen_cpp", + srcs = ["integration_test_gen.cpp"], + hdrs = ["integration_test_gen.h"], + features = COMPILER_WARNING_FEATURES, + implementation_deps = [ + "//score/mw/com/impl/rust/com-api/com-api-ffi-lola:registry_bridge_macro_cpp", + ], + alwayslink = True, +) + +rust_library( + name = "integration-test-lola", + srcs = [ + "integration_test.rs", + "lib.rs", + "test_types.rs", + ], + visibility = [ + "//visibility:public", # platform_only + ], + deps = [ + ":integration_test_gen_cpp", + "//score/mw/com/impl/rust/com-api/com-api", + "//score/mw/com/impl/rust/com-api/com-api-runtime-lola", + ], +) + +rust_test( + name = "integration-test", + crate = ":integration-test-lola", + data = [":integration_test_config"], + features = ["link_std_cpp_lib"], + deps = [ + ":integration-test-lola", + ":integration_test_gen_cpp", + "//score/mw/com/impl/rust/com-api/com-api", + "//score/mw/com/impl/rust/com-api/com-api-runtime-lola", + ], +) diff --git a/score/mw/com/impl/rust/com-api/com-api-integration-lola-test/config.json b/score/mw/com/impl/rust/com-api/com-api-integration-lola-test/config.json new file mode 100644 index 000000000..990db5d3c --- /dev/null +++ b/score/mw/com/impl/rust/com-api/com-api-integration-lola-test/config.json @@ -0,0 +1,112 @@ +{ + "serviceTypes": [ + { + "serviceTypeName": "/score/test/MixedPrimitivesInterface", + "version": { + "major": 1, + "minor": 0 + }, + "bindings": [ + { + "binding": "SHM", + "serviceId": 7001, + "events": [ + { + "eventName": "mixed_event", + "eventId": 1 + } + ] + } + ] + }, + { + "serviceTypeName": "/score/test/ComplexStructInterface", + "version": { + "major": 1, + "minor": 0 + }, + "bindings": [ + { + "binding": "SHM", + "serviceId": 7013, + "events": [ + { + "eventName": "complex_event", + "eventId": 1 + } + ] + } + ] + } + ], + "serviceInstances": [ + { + "instanceSpecifier": "/IntegrationTest/MixedPrimitives", + "serviceTypeName": "/score/test/MixedPrimitivesInterface", + "version": { + "major": 1, + "minor": 0 + }, + "instances": [ + { + "instanceId": 1, + "allowedConsumer": { + "QM": [ + 4002, + 0 + ] + }, + "allowedProvider": { + "QM": [ + 4001, + 0 + ] + }, + "asil-level": "QM", + "binding": "SHM", + "events": [ + { + "eventName": "mixed_event", + "numberOfSampleSlots": 10, + "maxSubscribers": 3 + } + ] + } + ] + }, + { + "instanceSpecifier": "/UserDefinedTest/ComplexStruct", + "serviceTypeName": "/score/test/ComplexStructInterface", + "version": { + "major": 1, + "minor": 0 + }, + "instances": [ + { + "instanceId": 13, + "allowedConsumer": { + "QM": [ + 4002, + 0 + ] + }, + "allowedProvider": { + "QM": [ + 4001, + 0 + ] + }, + "asil-level": "QM", + "binding": "SHM", + "events": [ + { + "eventName": "complex_event", + "numberOfSampleSlots": 10, + "maxSubscribers": 3 + } + ] + } + ] + } + ] +} diff --git a/score/mw/com/impl/rust/com-api/com-api-integration-lola-test/integration_test.rs b/score/mw/com/impl/rust/com-api/com-api-integration-lola-test/integration_test.rs new file mode 100644 index 000000000..ca10da264 --- /dev/null +++ b/score/mw/com/impl/rust/com-api/com-api-integration-lola-test/integration_test.rs @@ -0,0 +1,130 @@ +/******************************************************************************** + * 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 module covering both primitive and user-defined payloads. +//! +//! Primitive types are validated through one combined `MixedPrimitivesPayload` +//! Complex user-defined types are validated through `ComplexStruct` +//! The tests ensure that the COM API correctly handles data transmission and reception +//! for both primitive and complex types when integrated with the Lola runtime. +#[cfg(test)] +mod integration_tests { + use crate::generate_test; + use crate::test_types::{ + ArrayStruct, ComplexStruct, ComplexStructInterface, MixedPrimitivesInterface, + MixedPrimitivesPayload, NestedStruct, Point, Point3D, SensorData, SimpleStruct, + VehicleState, + }; + + generate_test!( + test = test_mixed_primitives_send_receive, + interface = MixedPrimitivesInterface, + event = mixed_event, + path = "/IntegrationTest/MixedPrimitives", + data = MixedPrimitivesPayload { + u64_val: u64::MAX, + i64_val: i64::MIN, + u32_val: u32::MAX, + i32_val: i32::MIN, + f32_val: std::f32::consts::E, + u16_val: u16::MAX, + i16_val: i16::MIN, + u8_val: u8::MAX, + i8_val: i8::MIN, + flag: true, + }, + assert = |r: &MixedPrimitivesPayload, e: &MixedPrimitivesPayload| { + assert_eq!(r.u64_val, e.u64_val, "u64_val mismatch"); + assert_eq!(r.i64_val, e.i64_val, "i64_val mismatch"); + assert_eq!(r.u32_val, e.u32_val, "u32_val mismatch"); + assert_eq!(r.i32_val, e.i32_val, "i32_val mismatch"); + assert!( + (r.f32_val - e.f32_val).abs() < 1e-6_f32, + "f32_val mismatch: {} vs {}", + r.f32_val, + e.f32_val + ); + assert_eq!(r.u16_val, e.u16_val, "u16_val mismatch"); + assert_eq!(r.i16_val, e.i16_val, "i16_val mismatch"); + assert_eq!(r.u8_val, e.u8_val, "u8_val mismatch"); + assert_eq!(r.i8_val, e.i8_val, "i8_val mismatch"); + assert_eq!(r.flag, e.flag, "flag mismatch"); + }, + ); + + generate_test!( + test = test_complex_struct_send_receive, + interface = ComplexStructInterface, + event = complex_event, + path = "/UserDefinedTest/ComplexStruct", + data = ComplexStruct { + count: 42, + simple: SimpleStruct { id: 10 }, + nested: NestedStruct { + id: 1, + simple: SimpleStruct { id: 123 }, + value: 99.9 + }, + point: Point { x: 1.5, y: 2.5 }, + point3d: Point3D { + x: 1.0, + y: 2.0, + z: 3.0 + }, + sensor: SensorData { + sensor_id: 7, + temperature: 22.5, + humidity: 45.0, + pressure: 1013.25 + }, + vehicle: VehicleState { + speed: 60.0, + rpm: 3000, + fuel_level: 75.0, + is_running: 1, + mileage: 50_000 + }, + array: ArrayStruct { + values: [1, 2, 3, 4, 5] + } + }, + assert = |r: &ComplexStruct, e: &ComplexStruct| { + assert_eq!(r.count, e.count); + assert_eq!(r.simple.id, e.simple.id); + assert_eq!(r.nested.id, e.nested.id); + assert_eq!(r.nested.simple.id, e.nested.simple.id); + assert!((r.nested.value - e.nested.value).abs() < 0.01_f32); + assert!((r.point.x - e.point.x).abs() < 0.001_f32); + assert!((r.point.y - e.point.y).abs() < 0.001_f32); + assert!((r.point3d.x - e.point3d.x).abs() < 0.001_f32); + assert!((r.point3d.y - e.point3d.y).abs() < 0.001_f32); + assert!((r.point3d.z - e.point3d.z).abs() < 0.001_f32); + assert_eq!(r.sensor.sensor_id, e.sensor.sensor_id); + assert!((r.sensor.temperature - e.sensor.temperature).abs() < 0.01_f32); + assert!((r.sensor.humidity - e.sensor.humidity).abs() < 0.01_f32); + assert!((r.sensor.pressure - e.sensor.pressure).abs() < 0.01_f32); + assert!((r.vehicle.speed - e.vehicle.speed).abs() < 0.1_f32); + assert_eq!(r.vehicle.rpm, e.vehicle.rpm); + assert!((r.vehicle.fuel_level - e.vehicle.fuel_level).abs() < 0.1_f32); + assert_eq!(r.vehicle.is_running, e.vehicle.is_running); + assert_eq!(r.vehicle.mileage, e.vehicle.mileage); + for i in 0..r.array.values.len() { + assert_eq!( + r.array.values[i], e.array.values[i], + "array.values[{}] mismatch", + i + ); + } + }, + ); +} diff --git a/score/mw/com/impl/rust/com-api/com-api-integration-lola-test/integration_test_gen.cpp b/score/mw/com/impl/rust/com-api/com-api-integration-lola-test/integration_test_gen.cpp new file mode 100644 index 000000000..186a32dbc --- /dev/null +++ b/score/mw/com/impl/rust/com-api/com-api-integration-lola-test/integration_test_gen.cpp @@ -0,0 +1,31 @@ +/******************************************************************************** + * 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 "integration_test_gen.h" +#include "score/mw/com/impl/rust/com-api/com-api-ffi-lola/registry_bridge_macro.h" + +BEGIN_EXPORT_MW_COM_INTERFACE(MixedPrimitivesInterface, + ::score::mw::com::integration_test::MixedPrimitivesProxy, + ::score::mw::com::integration_test::MixedPrimitivesSkeleton) +EXPORT_MW_COM_EVENT(::score::mw::com::integration_test::MixedPrimitivesPayload, mixed_event) +END_EXPORT_MW_COM_INTERFACE() + +BEGIN_EXPORT_MW_COM_INTERFACE(ComplexStructInterface, + ::score::mw::com::integration_test::ComplexStructProxy, + ::score::mw::com::integration_test::ComplexStructSkeleton) +EXPORT_MW_COM_EVENT(::score::mw::com::integration_test::ComplexStruct, complex_event) +END_EXPORT_MW_COM_INTERFACE() + +// Export all types +EXPORT_MW_COM_TYPE(MixedPrimitivesPayload, ::score::mw::com::integration_test::MixedPrimitivesPayload) +EXPORT_MW_COM_TYPE(ComplexStruct, ::score::mw::com::integration_test::ComplexStruct) diff --git a/score/mw/com/impl/rust/com-api/com-api-integration-lola-test/integration_test_gen.h b/score/mw/com/impl/rust/com-api/com-api-integration-lola-test/integration_test_gen.h new file mode 100644 index 000000000..4a32ed5e0 --- /dev/null +++ b/score/mw/com/impl/rust/com-api/com-api-integration-lola-test/integration_test_gen.h @@ -0,0 +1,120 @@ +/******************************************************************************** + * 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_RUST_COM_API_INTEGRATION_TEST_LOLA_INTEGRATION_TEST_GEN_H +#define SCORE_MW_COM_IMPL_RUST_COM_API_INTEGRATION_TEST_LOLA_INTEGRATION_TEST_GEN_H + +#include "score/mw/com/types.h" + +#include +#include + +namespace score::mw::com::integration_test +{ + +/// Combined payload carrying every primitive type in a single struct. +/// Field order follows descending alignment to eliminate padding bytes. +struct MixedPrimitivesPayload +{ + uint64_t u64_val; + int64_t i64_val; + uint32_t u32_val; + int32_t i32_val; + float f32_val; + uint16_t u16_val; + int16_t i16_val; + uint8_t u8_val; + int8_t i8_val; + bool flag; +}; + +struct SimpleStruct +{ + uint32_t id; +}; + +struct NestedStruct +{ + uint32_t id; + SimpleStruct simple; + float value; +}; + +struct Point +{ + float x; + float y; +}; + +struct Point3D +{ + float x; + float y; + float z; +}; + +struct SensorData +{ + uint16_t sensor_id; + float temperature; + float humidity; + float pressure; +}; + +struct VehicleState +{ + float speed; + uint16_t rpm; + float fuel_level; + uint8_t is_running; + uint32_t mileage; +}; + +struct ArrayStruct +{ + uint32_t values[5]; +}; + +struct ComplexStruct +{ + uint32_t count; + SimpleStruct simple; + NestedStruct nested; + Point point; + Point3D point3d; + SensorData sensor; + VehicleState vehicle; + ArrayStruct array; +}; + +// Macro to generate interface classes +#define CREATE_INTERFACE(ClassName, DataType, EventName) \ + template \ + class ClassName : public Trait::Base \ + { \ + public: \ + using Trait::Base::Base; \ + typename Trait::template Event EventName{*this, #EventName}; \ + }; + +// payload interface +CREATE_INTERFACE(MixedPrimitivesInterface, MixedPrimitivesPayload, mixed_event) +CREATE_INTERFACE(ComplexStructInterface, ComplexStruct, complex_event) + +// Type aliases for proxy and skeleton +using MixedPrimitivesProxy = ::score::mw::com::AsProxy; +using MixedPrimitivesSkeleton = ::score::mw::com::AsSkeleton; +using ComplexStructProxy = ::score::mw::com::AsProxy; +using ComplexStructSkeleton = ::score::mw::com::AsSkeleton; +} // namespace score::mw::com::integration_test +#endif // SCORE_MW_COM_IMPL_RUST_COM_API_INTEGRATION_TEST_LOLA_INTEGRATION_TEST_GEN_H diff --git a/score/mw/com/impl/rust/com-api/com-api-integration-lola-test/lib.rs b/score/mw/com/impl/rust/com-api/com-api-integration-lola-test/lib.rs new file mode 100644 index 000000000..349629225 --- /dev/null +++ b/score/mw/com/impl/rust/com-api/com-api-integration-lola-test/lib.rs @@ -0,0 +1,25 @@ +/******************************************************************************** + * 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 tests for COM API with Lola runtime +//! +//! This crate contains comprehensive integration tests for the COM API when used with the Lola runtime implementation. +//! The tests cover various aspects of the COM API, including producer and consumer interactions, data transmission, and service discovery. +//! The tests are designed to validate the correct functioning of the COM API abstractions when interfacing with the Lola middleware, +//! ensuring that data is correctly produced, transmitted, and consumed across different scenarios. +//! The test suite includes cases for primitive data types as well as user-defined types. + +pub mod test_types; + +#[cfg(test)] +mod integration_test; diff --git a/score/mw/com/impl/rust/com-api/com-api-integration-lola-test/test_types.rs b/score/mw/com/impl/rust/com-api/com-api-integration-lola-test/test_types.rs new file mode 100644 index 000000000..3c2975394 --- /dev/null +++ b/score/mw/com/impl/rust/com-api/com-api-integration-lola-test/test_types.rs @@ -0,0 +1,140 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ + +//! Test types and interfaces for integration testing. +use com_api::{interface, CommData, ProviderInfo, Publisher, Reloc, Subscriber}; + +// Macro to generate data types with CommData impl with Eq trait +macro_rules! define_type { + ($name:ident, eq, $($field:ident: $field_type:ty),+) => { + #[derive(Debug, Clone, Copy, PartialEq, Eq, Reloc)] + #[repr(C)] + pub struct $name { + $(pub $field: $field_type,)* + } + impl CommData for $name { + const ID: &'static str = stringify!($name); + } + }; + // Macro for types without Eq trait (e.g., with float fields) + ($name:ident, $($field:ident: $field_type:ty),+) => { + #[derive(Debug, Clone, Copy, PartialEq, Reloc)] + #[repr(C)] + pub struct $name { + $(pub $field: $field_type,)* + } + impl CommData for $name { + const ID: &'static str = stringify!($name); + } + }; +} + +// Combined primitive payload – all primitive types packed into one struct. +define_type!( + MixedPrimitivesPayload, + u64_val: u64, + i64_val: i64, + u32_val: u32, + i32_val: i32, + f32_val: f32, + u16_val: u16, + i16_val: i16, + u8_val: u8, + i8_val: i8, + flag: bool +); + +// Complex struct types +define_type!(SimpleStruct, eq, id: u32); +define_type!(NestedStruct, id: u32, simple: SimpleStruct, value: f32); +define_type!(Point, x: f32, y: f32); +define_type!(Point3D, x: f32, y: f32, z: f32); +define_type!(SensorData, sensor_id: u16, temperature: f32, humidity: f32, pressure: f32); +define_type!(VehicleState, speed: f32, rpm: u16, fuel_level: f32, is_running: u8, mileage: u32); +define_type!(ArrayStruct, eq, values: [u32; 5]); +define_type!(ComplexStruct, count: u32, simple: SimpleStruct, nested: NestedStruct, point: Point, point3d: Point3D, sensor: SensorData, vehicle: VehicleState, array: ArrayStruct); + +interface!(interface MixedPrimitives, { Id = "MixedPrimitivesInterface", mixed_event: Event }); +interface!(interface ComplexStruct, { Id = "ComplexStructInterface", complex_event: Event }); + +// Generic test macro for send/receive tests +#[macro_export] +macro_rules! generate_test { + ( + test = $test_fn:ident, + interface = $Iface:ident, + event = $event_field:ident, + path = $path:literal, + data = $test_data:expr, + assert = $assert:expr $(,)? + ) => { + #[test] + fn $test_fn() { + use com_api::{ + Builder, FindServiceSpecifier, InstanceSpecifier, LolaRuntimeBuilderImpl, + OfferedProducer, Producer, Publisher, Runtime, RuntimeBuilder, SampleContainer, + SampleMaybeUninit, SampleMut, ServiceDiscovery, Subscriber, Subscription, + }; + + let mut builder = LolaRuntimeBuilderImpl::new(); + builder.load_config(std::path::Path::new( + "score/mw/com/impl/rust/com-api/com-api-integration-lola-test/config.json", + )); + let runtime = builder.build().expect("Failed to build runtime"); + + let service_id = + InstanceSpecifier::new($path).expect("Failed to create InstanceSpecifier"); + + let producer = runtime + .producer_builder::<$Iface>(service_id.clone()) + .build() + .expect("Failed to build producer") + .offer() + .expect("Failed to offer producer"); + let consumer = runtime + .find_service::<$Iface>(FindServiceSpecifier::Specific(service_id)) + .get_available_instances() + .expect("Failed to get available instances") + .into_iter() + .next() + .expect("No service instances available") + .build() + .expect("Failed to build consumer"); + + let test_value = $test_data; + let uninit_sample = producer + .$event_field + .allocate() + .expect("Failed to allocate sample"); + let sample = uninit_sample.write(test_value); + sample.send().expect("Failed to send sample"); + + let subscription = consumer + .$event_field + .subscribe(1) + .expect("Failed to subscribe to event"); + let mut sample_buf = SampleContainer::new(1); + + match subscription.try_receive(&mut sample_buf, 1) { + Ok(received_count) => { + assert_eq!(received_count, 1, "Should receive exactly 1 sample"); + let received = sample_buf.pop_front().unwrap(); + ($assert)(&*received, &test_value); + } + Err(e) => panic!("Failed to receive data: {:?}", e), + } + + let _ = producer.unoffer().expect("Failed to unoffer producer"); + } + }; +}