From 9a9eeaad02785b48aaa14020739c6a71d4386145 Mon Sep 17 00:00:00 2001 From: nxtum Date: Fri, 2 Oct 2026 07:48:30 +0000 Subject: [PATCH] add vector test suite --- tests/FunctionalTests/CMakeLists.txt | 1 + tests/FunctionalTests/README.md | 1 + tests/FunctionalTests/common/CMakeLists.txt | 4 + .../implementations/TestVectorsImpl.cpp | 214 +++++++++++++++ .../common/interfaces/ITestVectors.h | 81 ++++++ tests/FunctionalTests/common/interfaces/Ids.h | 1 + .../common/interfaces/README.md | 14 +- tests/FunctionalTests/comrpc/CMakeLists.txt | 4 + .../comrpc/tests/TestVectors.cpp | 246 ++++++++++++++++++ 9 files changed, 562 insertions(+), 4 deletions(-) create mode 100644 tests/FunctionalTests/common/implementations/TestVectorsImpl.cpp create mode 100644 tests/FunctionalTests/common/interfaces/ITestVectors.h create mode 100644 tests/FunctionalTests/comrpc/tests/TestVectors.cpp diff --git a/tests/FunctionalTests/CMakeLists.txt b/tests/FunctionalTests/CMakeLists.txt index ffc317be..aefd7c5f 100644 --- a/tests/FunctionalTests/CMakeLists.txt +++ b/tests/FunctionalTests/CMakeLists.txt @@ -25,6 +25,7 @@ option(TEST_OPTIONALS "Enable optional parameters tests" ON) option(TEST_PRIMITIVES "Enable primitive types tests" ON) option(TEST_RESTRICTIONS "Enable restrict annotation tests" ON) option(TEST_STRUCTS "Enable POD struct tests" ON) +option(TEST_VECTORS "Enable std::vector tests" ON) option(TEST_ENCODING_MAC "Enable @encode:mac annotation tests" ON) option(TEST_LENGTH_MODES "Enable @length:void/@length:return tests" ON) option(TEST_JSON_SHAPE "Enable @wrapped/@extract shape tests" ON) diff --git a/tests/FunctionalTests/README.md b/tests/FunctionalTests/README.md index 9f006fad..0bf03b42 100644 --- a/tests/FunctionalTests/README.md +++ b/tests/FunctionalTests/README.md @@ -14,6 +14,7 @@ The build is fully self-contained: Thunder and GoogleTest are fetched automatica | `TEST_RESTRICTIONS` | `ITestRestrictions` | ✓ | ✓ | `@restrict` range validation on integers (signed/unsigned), floats, strings (length), the `nonempty` shorthand, the `K`-suffix, and `OptionalType` combined with a range | | `TEST_ENUMS` | `ITestEnums` | ✓ | ✓ | Enum serialisation: `@encode:text`, `@encode:bitmask`, `@property`, in/out/inout parameters, optional enums | | `TEST_STRUCTS` | `ITestStructs` | ✓ | ✓ | POD struct marshalling: in/out/inout parameters, nested structs, `std::vector`, `@opaque`, `@index` (property slot), `@restrict` on vectors | +| `TEST_VECTORS` | `ITestVectors` | ✓ | — | `std::vector` input/output/inout marshalling for primitives, structs, nested vectors, optional elements, optional vectors, and vectors inside structs | | `TEST_EVENTS` | `ITestEvents` | ✓ | — | Event callback pattern (`@event`, `INotification`): scalar payloads, struct payloads, `std::vector` payloads, `OptionalType` payloads, `@statuslistener` | | `TEST_ASYNC` | `ITestAsync` | ✓ | ✓ | `@async` pattern: concurrent slots, `ICallback` interface, `@property` with `@index`, `OptionalType` in a callback | | `TEST_INTERFACES` | `ITestInterfaces` | ✓ | — | Generator control annotations: `@interface` (void\* + ID dynamic typing), `@stub` (server-side only), `@omit` (excluded from both proxy and stub) | diff --git a/tests/FunctionalTests/common/CMakeLists.txt b/tests/FunctionalTests/common/CMakeLists.txt index 29f36b46..4555a38d 100644 --- a/tests/FunctionalTests/common/CMakeLists.txt +++ b/tests/FunctionalTests/common/CMakeLists.txt @@ -184,6 +184,10 @@ if (TEST_STRUCTS) AddTestInterface("Structs" COM_RPC JSON_RPC) endif() +if (TEST_VECTORS) + AddTestInterface("Vectors" COM_RPC) # COM-RPC only — JsonGenerator does not support nested vectors +endif() + if (TEST_RESTRICTIONS) AddTestInterface("Restrictions" COM_RPC JSON_RPC) endif() diff --git a/tests/FunctionalTests/common/implementations/TestVectorsImpl.cpp b/tests/FunctionalTests/common/implementations/TestVectorsImpl.cpp new file mode 100644 index 00000000..9ca36a39 --- /dev/null +++ b/tests/FunctionalTests/common/implementations/TestVectorsImpl.cpp @@ -0,0 +1,214 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2026 Metrological + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +namespace Thunder { +namespace TestImplementation { + + using namespace FunctionalTest; + + class TestVectorsImpl : public ITestVectors { + public: + TestVectorsImpl() = default; + ~TestVectorsImpl() override = default; + + TestVectorsImpl(const TestVectorsImpl&) = delete; + TestVectorsImpl& operator=(const TestVectorsImpl&) = delete; + + Core::hresult SumPrimitives(const std::vector& values, uint32_t& sum) const override + { + sum = 0; + for (const auto value : values) { + sum += value; + } + return Core::ERROR_NONE; + } + + Core::hresult GetPrimitives(std::vector& values) const override + { + values = { 1, 2, 3, 5, 8 }; + return Core::ERROR_NONE; + } + + Core::hresult TransformPrimitives(std::vector& values) const override + { + std::reverse(values.begin(), values.end()); + for (auto& value : values) { + ++value; + } + return Core::ERROR_NONE; + } + + Core::hresult SummarizeEntries(const std::vector& values, uint32_t& idSum, string& concatenatedNames) const override + { + idSum = 0; + concatenatedNames.clear(); + for (const auto& entry : values) { + idSum += entry.id; + concatenatedNames += entry.name; + } + return Core::ERROR_NONE; + } + + Core::hresult GetEntries(std::vector& values) const override + { + values = { { 7, "seven" }, { 11, "eleven" } }; + return Core::ERROR_NONE; + } + + Core::hresult TransformEntries(std::vector& values) const override + { + for (auto& entry : values) { + entry.id += 100; + entry.name += "!"; + } + return Core::ERROR_NONE; + } + + Core::hresult SummarizeNestedVectors(const std::vector>& values, uint32_t& valueSum, uint32_t& vectorCount) const override + { + valueSum = 0; + vectorCount = static_cast(values.size()); + for (const auto& innerValues : values) { + for (const auto value : innerValues) { + valueSum += value; + } + } + return Core::ERROR_NONE; + } + + Core::hresult GetNestedVectors(std::vector>& values) const override + { + values = { { 1, 2 }, {}, { 3, 4, 5 } }; + return Core::ERROR_NONE; + } + + Core::hresult TransformNestedVectors(std::vector>& values) const override + { + for (auto& innerValues : values) { + std::reverse(innerValues.begin(), innerValues.end()); + } + std::reverse(values.begin(), values.end()); + return Core::ERROR_NONE; + } + + Core::hresult SummarizeOptionalElements(const std::vector>& values, uint32_t& valueSum, uint32_t& setCount) const override + { + valueSum = 0; + setCount = 0; + for (const auto& value : values) { + if (value.IsSet()) { + valueSum += value.Value(); + ++setCount; + } + } + return Core::ERROR_NONE; + } + + Core::hresult GetOptionalElements(std::vector>& values) const override + { + values = { + Core::OptionalType(2), + Core::OptionalType(), + Core::OptionalType(6) + }; + return Core::ERROR_NONE; + } + + Core::hresult TransformOptionalElements(std::vector>& values) const override + { + for (auto& value : values) { + if (value.IsSet()) { + value = value.Value() + 1; + } + } + return Core::ERROR_NONE; + } + + Core::hresult SummarizeOptionalVector(const Core::OptionalType>& values, bool& isSet, uint32_t& valueSum) const override + { + isSet = values.IsSet(); + valueSum = 0; + if (isSet) { + for (const auto value : values.Value()) { + valueSum += value; + } + } + return Core::ERROR_NONE; + } + + Core::hresult GetOptionalVector(Core::OptionalType>& values) const override + { + values = std::vector { 2, 4, 6 }; + return Core::ERROR_NONE; + } + + Core::hresult TransformOptionalVector(Core::OptionalType>& values) const override + { + if (values.IsSet()) { + std::reverse(values.Value().begin(), values.Value().end()); + for (auto& value : values.Value()) { + ++value; + } + } + return Core::ERROR_NONE; + } + + Core::hresult EchoCollection(const Collection& input, Collection& output) const override + { + output = input; + return Core::ERROR_NONE; + } + + Core::hresult GetCollection(Collection& collection) const override + { + collection.primitives = { 2, 4 }; + collection.entries = { { 6, "six" } }; + collection.nestedVectors = { { 8, 10 }, { 12 } }; + return Core::ERROR_NONE; + } + + Core::hresult TransformCollection(Collection& collection) const override + { + for (auto& value : collection.primitives) { + ++value; + } + for (auto& entry : collection.entries) { + entry.id += 100; + entry.name += "!"; + } + for (auto& values : collection.nestedVectors) { + std::reverse(values.begin(), values.end()); + } + return Core::ERROR_NONE; + } + + BEGIN_INTERFACE_MAP(TestVectorsImpl) + INTERFACE_ENTRY(ITestVectors) + END_INTERFACE_MAP + }; + + static Factory::Registrar g_vectorsRegistrar; + +} // namespace TestImplementation +} // namespace Thunder \ No newline at end of file diff --git a/tests/FunctionalTests/common/interfaces/ITestVectors.h b/tests/FunctionalTests/common/interfaces/ITestVectors.h new file mode 100644 index 00000000..3a4c49fa --- /dev/null +++ b/tests/FunctionalTests/common/interfaces/ITestVectors.h @@ -0,0 +1,81 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2026 Metrological + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "Ids.h" +#include "Module.h" + +namespace Thunder { +namespace FunctionalTest { + + // Test std::vector serialization over COM-RPC. + struct EXTERNAL ITestVectors : virtual public Core::IUnknown { + enum { ID = ID_TEST_VECTORS }; + + struct Entry { + uint32_t id; + string name; + }; + + struct Collection { + std::vector primitives /* @restrict:0..255 */; + std::vector entries /* @restrict:0..255 */; + std::vector /* @restrict:0..255 */> nestedVectors /* @restrict:0..255 */; + }; + + // ===== Primitive-element vectors ===== + + virtual Core::hresult SumPrimitives(const std::vector& values /* @in @restrict:0..255 */, uint32_t& sum /* @out */) const = 0; + virtual Core::hresult GetPrimitives(std::vector& values /* @out @restrict:0..255 */) const = 0; + virtual Core::hresult TransformPrimitives(std::vector& values /* @inout @restrict:0..255 */) const = 0; + + // ===== Struct-element vectors ===== + + virtual Core::hresult SummarizeEntries(const std::vector& values /* @in @restrict:0..255 */, uint32_t& idSum /* @out */, string& concatenatedNames /* @out */) const = 0; + virtual Core::hresult GetEntries(std::vector& values /* @out @restrict:0..255 */) const = 0; + virtual Core::hresult TransformEntries(std::vector& values /* @inout @restrict:0..255 */) const = 0; + + // ===== Nested vectors ===== + + virtual Core::hresult SummarizeNestedVectors(const std::vector /* @restrict:0..255 */>& values /* @in @restrict:0..255 */, uint32_t& valueSum /* @out */, uint32_t& vectorCount /* @out */) const = 0; + virtual Core::hresult GetNestedVectors(std::vector /* @restrict:0..255 */>& values /* @out @restrict:0..255 */) const = 0; + virtual Core::hresult TransformNestedVectors(std::vector /* @restrict:0..255 */>& values /* @inout @restrict:0..255 */) const = 0; + + // ===== Vectors of optional elements ===== + + virtual Core::hresult SummarizeOptionalElements(const std::vector>& values /* @in @restrict:0..255 */, uint32_t& valueSum /* @out */, uint32_t& setCount /* @out */) const = 0; + virtual Core::hresult GetOptionalElements(std::vector>& values /* @out @restrict:0..255 */) const = 0; + virtual Core::hresult TransformOptionalElements(std::vector>& values /* @inout @restrict:0..255 */) const = 0; + + // ===== Optional vectors ===== + + virtual Core::hresult SummarizeOptionalVector(const Core::OptionalType>& values /* @in @restrict:0..255 */, bool& isSet /* @out */, uint32_t& valueSum /* @out */) const = 0; + virtual Core::hresult GetOptionalVector(Core::OptionalType>& values /* @out @restrict:0..255 */) const = 0; + virtual Core::hresult TransformOptionalVector(Core::OptionalType>& values /* @inout @restrict:0..255 */) const = 0; + + // ===== Vectors as struct members ===== + + virtual Core::hresult EchoCollection(const Collection& input /* @in */, Collection& output /* @out */) const = 0; + virtual Core::hresult GetCollection(Collection& collection /* @out */) const = 0; + virtual Core::hresult TransformCollection(Collection& collection /* @inout */) const = 0; + }; + +} // namespace FunctionalTest +} // namespace Thunder diff --git a/tests/FunctionalTests/common/interfaces/Ids.h b/tests/FunctionalTests/common/interfaces/Ids.h index 5cef249f..02475a76 100644 --- a/tests/FunctionalTests/common/interfaces/Ids.h +++ b/tests/FunctionalTests/common/interfaces/Ids.h @@ -54,6 +54,7 @@ namespace Thunder { ID_TEST_ANNOTATION_EVENTS_NOTIFICATION = ID_INTERFACE_OFFSET + 0x019, ID_TEST_PREFIX_UNDERSCORE = ID_INTERFACE_OFFSET + 0x01A, ID_TEST_WRAPPED_INTERFACE = ID_INTERFACE_OFFSET + 0x01B, + ID_TEST_VECTORS = ID_INTERFACE_OFFSET + 0x01C, }; } // namespace FunctionalTest diff --git a/tests/FunctionalTests/common/interfaces/README.md b/tests/FunctionalTests/common/interfaces/README.md index d49c094d..fc7bb32a 100644 --- a/tests/FunctionalTests/common/interfaces/README.md +++ b/tests/FunctionalTests/common/interfaces/README.md @@ -24,6 +24,9 @@ Covers the `@restrict` annotation across all value kinds: exact-value restrictio ### ITestStructs Validates POD struct marshalling: nested structs (`Rectangle` contains `Point`), structs as `@in`/`@out`/`@inout` parameters, multi-struct calls, `std::vector` with `@restrict`, `@property` with `@index` for per-slot addressing, and `@opaque` for raw JSON pass-through. +### ITestVectors +Validates COM-RPC `std::vector` marshalling in every parameter direction for primitive, struct, nested-vector, and optional elements; optional vectors; and vector members in structs. Every vector uses the full `0..255` range of its 8-bit wire length because ProxyStubGenerator requires a maximum bound on wire-visible vectors. + ### ITestIterators Validates `RPC::IStringIterator` and `RPC::IValueIterator` lifecycle: creation (normal, empty, large), and the store-and-query pattern that avoids COM-RPC channel deadlock. Clients hand an iterator to the server (which drains it synchronously), then query the cached results via a separate call — keeping the channel free for callbacks. Not tagged `@json 1.0.0` as iterators have no clean JSON mapping. @@ -82,9 +85,9 @@ Pins the generator behaviour for `@uncompliant:collapsed` (deprecated — do not | `@restrict` (string length) | ITestRestrictions | | `@restrict:nonempty` | ITestRestrictions | | `@restrict:K` suffix | ITestRestrictions | -| `@restrict` on `std::vector` | ITestStructs, ITestEvents | +| `@restrict` on `std::vector` | ITestStructs, ITestVectors, ITestEvents | | `@default` | ITestAsync, ITestOptionals | -| `@optional` / `Core::OptionalType` | ITestAsync, ITestOptionals, ITestEnums, ITestEvents, ITestRestrictions | +| `@optional` / `Core::OptionalType` | ITestAsync, ITestOptionals, ITestVectors, ITestEnums, ITestEvents, ITestRestrictions | | `@property` (read-write) | ITestEnums, ITestStructs, ITestAsync, ITestJsonUncompliantExtended, ITestJsonUncompliantCollapsed | | `@property` (read-only) | ITestEnums, ITestAsync | | `@property` (write-only) | ITestEnums | @@ -105,8 +108,11 @@ Pins the generator behaviour for `@uncompliant:collapsed` (deprecated — do not | `@uncompliant:extended` ⚠️ deprecated | ITestJsonUncompliantExtended | | `@uncompliant:collapsed` ⚠️ deprecated | ITestJsonUncompliantCollapsed | | `@json 1.0.0` | ITestPrimitives, ITestEnums, ITestOptionals, ITestRestrictions, ITestStructs, ITestEvents, ITestAsync, ITestEncodingMac, ITestJsonShape, ITestJsonTextKeep, ITestJsonTextCase, ITestJsonCompliant, ITestJsonUncompliantExtended, ITestJsonUncompliantCollapsed | -| `std::vector` | ITestStructs | -| `std::vector` | ITestStructs, ITestEvents | +| `std::vector` | ITestStructs, ITestVectors | +| `std::vector` | ITestStructs, ITestVectors, ITestEvents | +| `std::vector>` | ITestVectors | +| `std::vector>` | ITestVectors | +| `Core::OptionalType>` | ITestOptionals, ITestVectors | | `RPC::IStringIterator` | ITestIterators | | `RPC::IValueIterator` | ITestIterators | diff --git a/tests/FunctionalTests/comrpc/CMakeLists.txt b/tests/FunctionalTests/comrpc/CMakeLists.txt index b50e140b..b2072b30 100644 --- a/tests/FunctionalTests/comrpc/CMakeLists.txt +++ b/tests/FunctionalTests/comrpc/CMakeLists.txt @@ -59,6 +59,10 @@ if(TEST_STRUCTS) target_sources(ComRpcFunctionalTests PRIVATE tests/TestStructs.cpp) endif() +if(TEST_VECTORS) + target_sources(ComRpcFunctionalTests PRIVATE tests/TestVectors.cpp) +endif() + if(TEST_ENCODING_MAC) target_sources(ComRpcFunctionalTests PRIVATE tests/TestEncodingMac.cpp) endif() diff --git a/tests/FunctionalTests/comrpc/tests/TestVectors.cpp b/tests/FunctionalTests/comrpc/tests/TestVectors.cpp new file mode 100644 index 00000000..74b68881 --- /dev/null +++ b/tests/FunctionalTests/comrpc/tests/TestVectors.cpp @@ -0,0 +1,246 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2026 Metrological + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "TestHarness.h" +#include + +using namespace Thunder; +using namespace Thunder::FunctionalTest; + +class TestVectors : public Testing::TestHarness {}; + +// Get-method outputs are seeded to verify that decoding replaces existing contents. + +// ===== Primitive-element vectors ===== + +TEST_F(TestVectors, SumPrimitives) { + uint32_t sum = 0; + ASSERT_EQ(_proxy->SumPrimitives({ 1, 2, 3, 4 }, sum), Core::ERROR_NONE); + EXPECT_EQ(sum, 10u); +} + +TEST_F(TestVectors, GetPrimitives) { + std::vector values { 99 }; + ASSERT_EQ(_proxy->GetPrimitives(values), Core::ERROR_NONE); + EXPECT_EQ(values, (std::vector { 1, 2, 3, 5, 8 })); +} + +TEST_F(TestVectors, TransformPrimitives) { + std::vector values { 1, 2, 3 }; + ASSERT_EQ(_proxy->TransformPrimitives(values), Core::ERROR_NONE); + EXPECT_EQ(values, (std::vector { 4, 3, 2 })); +} + +// ===== Struct-element vectors ===== + +TEST_F(TestVectors, SummarizeEntries) { + uint32_t idSum = 0; + string concatenatedNames; + ASSERT_EQ(_proxy->SummarizeEntries({ { 3, "three" }, { 5, "five" } }, idSum, concatenatedNames), Core::ERROR_NONE); + EXPECT_EQ(idSum, 8u); + EXPECT_EQ(concatenatedNames, "threefive"); +} + +TEST_F(TestVectors, GetEntries) { + std::vector values { { 99, "stale" } }; + ASSERT_EQ(_proxy->GetEntries(values), Core::ERROR_NONE); + ASSERT_EQ(values.size(), 2u); + EXPECT_EQ(values[0].id, 7u); + EXPECT_EQ(values[0].name, "seven"); + EXPECT_EQ(values[1].id, 11u); + EXPECT_EQ(values[1].name, "eleven"); +} + +TEST_F(TestVectors, TransformEntries) { + std::vector values { { 1, "one" }, { 2, "two" } }; + ASSERT_EQ(_proxy->TransformEntries(values), Core::ERROR_NONE); + ASSERT_EQ(values.size(), 2u); + EXPECT_EQ(values[0].id, 101u); + EXPECT_EQ(values[0].name, "one!"); + EXPECT_EQ(values[1].id, 102u); + EXPECT_EQ(values[1].name, "two!"); +} + +// ===== Nested vectors ===== + +TEST_F(TestVectors, SummarizeNestedVectors) { + uint32_t valueSum = 0; + uint32_t vectorCount = 0; + ASSERT_EQ(_proxy->SummarizeNestedVectors({ { 1, 2 }, {}, { 3, 4, 5 } }, valueSum, vectorCount), Core::ERROR_NONE); + EXPECT_EQ(valueSum, 15u); + EXPECT_EQ(vectorCount, 3u); +} + +TEST_F(TestVectors, GetNestedVectors) { + std::vector> values { { 99 } }; + ASSERT_EQ(_proxy->GetNestedVectors(values), Core::ERROR_NONE); + EXPECT_EQ(values, (std::vector> { { 1, 2 }, {}, { 3, 4, 5 } })); +} + +TEST_F(TestVectors, TransformNestedVectors) { + std::vector> values { { 1, 2 }, {}, { 3, 4, 5 } }; + ASSERT_EQ(_proxy->TransformNestedVectors(values), Core::ERROR_NONE); + EXPECT_EQ(values, (std::vector> { { 5, 4, 3 }, {}, { 2, 1 } })); +} + +// ===== Vectors of optional elements ===== + +TEST_F(TestVectors, SummarizeOptionalElements) { + const std::vector> values { + Core::OptionalType(3), + Core::OptionalType(), + Core::OptionalType(5) + }; + uint32_t valueSum = 0; + uint32_t setCount = 0; + + ASSERT_EQ(_proxy->SummarizeOptionalElements(values, valueSum, setCount), Core::ERROR_NONE); + EXPECT_EQ(valueSum, 8u); + EXPECT_EQ(setCount, 2u); +} + +TEST_F(TestVectors, GetOptionalElements) { + std::vector> values { Core::OptionalType(99) }; + ASSERT_EQ(_proxy->GetOptionalElements(values), Core::ERROR_NONE); + + ASSERT_EQ(values.size(), 3u); + ASSERT_TRUE(values[0].IsSet()); + EXPECT_EQ(values[0].Value(), 2u); + EXPECT_FALSE(values[1].IsSet()); + ASSERT_TRUE(values[2].IsSet()); + EXPECT_EQ(values[2].Value(), 6u); +} + +TEST_F(TestVectors, TransformOptionalElements) { + std::vector> values { + Core::OptionalType(1), + Core::OptionalType(), + Core::OptionalType(3) + }; + ASSERT_EQ(_proxy->TransformOptionalElements(values), Core::ERROR_NONE); + + ASSERT_EQ(values.size(), 3u); + ASSERT_TRUE(values[0].IsSet()); + EXPECT_EQ(values[0].Value(), 2u); + EXPECT_FALSE(values[1].IsSet()); + ASSERT_TRUE(values[2].IsSet()); + EXPECT_EQ(values[2].Value(), 4u); +} + +// ===== Optional vectors ===== + +TEST_F(TestVectors, SummarizeOptionalVector_Set) { + Core::OptionalType> values; + values = std::vector { 1, 2, 3 }; + bool isSet = false; + uint32_t valueSum = 0; + + ASSERT_EQ(_proxy->SummarizeOptionalVector(values, isSet, valueSum), Core::ERROR_NONE); + EXPECT_TRUE(isSet); + EXPECT_EQ(valueSum, 6u); +} + +TEST_F(TestVectors, SummarizeOptionalVector_Empty) { + Core::OptionalType> values; + values = std::vector {}; + bool isSet = false; + uint32_t valueSum = 99; + + ASSERT_EQ(_proxy->SummarizeOptionalVector(values, isSet, valueSum), Core::ERROR_NONE); + EXPECT_TRUE(isSet); + EXPECT_EQ(valueSum, 0u); +} + +TEST_F(TestVectors, SummarizeOptionalVector_Unset) { + Core::OptionalType> values; + bool isSet = true; + uint32_t valueSum = 99; + + ASSERT_EQ(_proxy->SummarizeOptionalVector(values, isSet, valueSum), Core::ERROR_NONE); + EXPECT_FALSE(isSet); + EXPECT_EQ(valueSum, 0u); +} + +TEST_F(TestVectors, GetOptionalVector) { + Core::OptionalType> values; + values = std::vector { 99 }; + ASSERT_EQ(_proxy->GetOptionalVector(values), Core::ERROR_NONE); + ASSERT_TRUE(values.IsSet()); + EXPECT_EQ(values.Value(), (std::vector { 2, 4, 6 })); +} + +TEST_F(TestVectors, TransformOptionalVector_Set) { + Core::OptionalType> values; + values = std::vector { 1, 2, 3 }; + ASSERT_EQ(_proxy->TransformOptionalVector(values), Core::ERROR_NONE); + ASSERT_TRUE(values.IsSet()); + EXPECT_EQ(values.Value(), (std::vector { 4, 3, 2 })); +} + +TEST_F(TestVectors, TransformOptionalVector_Unset) { + Core::OptionalType> values; + ASSERT_EQ(_proxy->TransformOptionalVector(values), Core::ERROR_NONE); + EXPECT_FALSE(values.IsSet()); +} + +// ===== Vectors as struct members ===== + +TEST_F(TestVectors, EchoCollection) { + ITestVectors::Collection input; + input.primitives = { 1, 2 }; + input.entries = { { 3, "three" } }; + input.nestedVectors = { { 4 }, { 5, 6 } }; + + ITestVectors::Collection output; + ASSERT_EQ(_proxy->EchoCollection(input, output), Core::ERROR_NONE); + EXPECT_EQ(output.primitives, (std::vector { 1, 2 })); + ASSERT_EQ(output.entries.size(), 1u); + EXPECT_EQ(output.entries[0].id, 3u); + EXPECT_EQ(output.entries[0].name, "three"); + EXPECT_EQ(output.nestedVectors, (std::vector> { { 4 }, { 5, 6 } })); +} + +TEST_F(TestVectors, GetCollection) { + ITestVectors::Collection collection; + collection.primitives = { 99 }; + collection.entries = { { 99, "stale" } }; + collection.nestedVectors = { { 99 } }; + + ASSERT_EQ(_proxy->GetCollection(collection), Core::ERROR_NONE); + EXPECT_EQ(collection.primitives, (std::vector { 2, 4 })); + ASSERT_EQ(collection.entries.size(), 1u); + EXPECT_EQ(collection.entries[0].id, 6u); + EXPECT_EQ(collection.entries[0].name, "six"); + EXPECT_EQ(collection.nestedVectors, (std::vector> { { 8, 10 }, { 12 } })); +} + +TEST_F(TestVectors, TransformCollection) { + ITestVectors::Collection collection; + collection.primitives = { 1, 2 }; + collection.entries = { { 3, "three" } }; + collection.nestedVectors = { { 4 }, { 5, 6 } }; + + ASSERT_EQ(_proxy->TransformCollection(collection), Core::ERROR_NONE); + EXPECT_EQ(collection.primitives, (std::vector { 2, 3 })); + ASSERT_EQ(collection.entries.size(), 1u); + EXPECT_EQ(collection.entries[0].id, 103u); + EXPECT_EQ(collection.entries[0].name, "three!"); + EXPECT_EQ(collection.nestedVectors, (std::vector> { { 4 }, { 6, 5 } })); +} \ No newline at end of file