Skip to content
Open
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
1 change: 1 addition & 0 deletions tests/FunctionalTests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions tests/FunctionalTests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<struct>`, `@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) |
Expand Down
4 changes: 4 additions & 0 deletions tests/FunctionalTests/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
214 changes: 214 additions & 0 deletions tests/FunctionalTests/common/implementations/TestVectorsImpl.cpp
Original file line number Diff line number Diff line change
@@ -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 <ImplementationFactory.h>
#include <ITestVectors.h>

#include <algorithm>

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<uint32_t>& values, uint32_t& sum) const override
{
sum = 0;
for (const auto value : values) {
sum += value;
}
return Core::ERROR_NONE;
}

Core::hresult GetPrimitives(std::vector<uint32_t>& values) const override
{
values = { 1, 2, 3, 5, 8 };
return Core::ERROR_NONE;
}

Core::hresult TransformPrimitives(std::vector<uint32_t>& values) const override
{
std::reverse(values.begin(), values.end());
for (auto& value : values) {
++value;
}
return Core::ERROR_NONE;
}

Core::hresult SummarizeEntries(const std::vector<Entry>& 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<Entry>& values) const override
{
values = { { 7, "seven" }, { 11, "eleven" } };
return Core::ERROR_NONE;
}

Core::hresult TransformEntries(std::vector<Entry>& values) const override
{
for (auto& entry : values) {
entry.id += 100;
entry.name += "!";
}
return Core::ERROR_NONE;
}

Core::hresult SummarizeNestedVectors(const std::vector<std::vector<uint32_t>>& values, uint32_t& valueSum, uint32_t& vectorCount) const override
{
valueSum = 0;
vectorCount = static_cast<uint32_t>(values.size());
for (const auto& innerValues : values) {
for (const auto value : innerValues) {
valueSum += value;
}
}
return Core::ERROR_NONE;
}

Core::hresult GetNestedVectors(std::vector<std::vector<uint32_t>>& values) const override
{
values = { { 1, 2 }, {}, { 3, 4, 5 } };
return Core::ERROR_NONE;
}

Core::hresult TransformNestedVectors(std::vector<std::vector<uint32_t>>& 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<Core::OptionalType<uint32_t>>& 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<Core::OptionalType<uint32_t>>& values) const override
{
values = {
Core::OptionalType<uint32_t>(2),
Core::OptionalType<uint32_t>(),
Core::OptionalType<uint32_t>(6)
};
return Core::ERROR_NONE;
}

Core::hresult TransformOptionalElements(std::vector<Core::OptionalType<uint32_t>>& values) const override
{
for (auto& value : values) {
if (value.IsSet()) {
value = value.Value() + 1;
}
}
return Core::ERROR_NONE;
}

Core::hresult SummarizeOptionalVector(const Core::OptionalType<std::vector<uint32_t>>& 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<std::vector<uint32_t>>& values) const override
{
values = std::vector<uint32_t> { 2, 4, 6 };
return Core::ERROR_NONE;
}

Core::hresult TransformOptionalVector(Core::OptionalType<std::vector<uint32_t>>& 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<ITestVectors, TestVectorsImpl> g_vectorsRegistrar;

} // namespace TestImplementation
} // namespace Thunder
81 changes: 81 additions & 0 deletions tests/FunctionalTests/common/interfaces/ITestVectors.h
Original file line number Diff line number Diff line change
@@ -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<uint32_t> primitives /* @restrict:0..255 */;
std::vector<Entry> entries /* @restrict:0..255 */;
std::vector<std::vector<uint32_t> /* @restrict:0..255 */> nestedVectors /* @restrict:0..255 */;
};

// ===== Primitive-element vectors =====

virtual Core::hresult SumPrimitives(const std::vector<uint32_t>& values /* @in @restrict:0..255 */, uint32_t& sum /* @out */) const = 0;
virtual Core::hresult GetPrimitives(std::vector<uint32_t>& values /* @out @restrict:0..255 */) const = 0;
virtual Core::hresult TransformPrimitives(std::vector<uint32_t>& values /* @inout @restrict:0..255 */) const = 0;

// ===== Struct-element vectors =====

virtual Core::hresult SummarizeEntries(const std::vector<Entry>& values /* @in @restrict:0..255 */, uint32_t& idSum /* @out */, string& concatenatedNames /* @out */) const = 0;
virtual Core::hresult GetEntries(std::vector<Entry>& values /* @out @restrict:0..255 */) const = 0;
virtual Core::hresult TransformEntries(std::vector<Entry>& values /* @inout @restrict:0..255 */) const = 0;

// ===== Nested vectors =====

virtual Core::hresult SummarizeNestedVectors(const std::vector<std::vector<uint32_t> /* @restrict:0..255 */>& values /* @in @restrict:0..255 */, uint32_t& valueSum /* @out */, uint32_t& vectorCount /* @out */) const = 0;
virtual Core::hresult GetNestedVectors(std::vector<std::vector<uint32_t> /* @restrict:0..255 */>& values /* @out @restrict:0..255 */) const = 0;
virtual Core::hresult TransformNestedVectors(std::vector<std::vector<uint32_t> /* @restrict:0..255 */>& values /* @inout @restrict:0..255 */) const = 0;

// ===== Vectors of optional elements =====

virtual Core::hresult SummarizeOptionalElements(const std::vector<Core::OptionalType<uint32_t>>& values /* @in @restrict:0..255 */, uint32_t& valueSum /* @out */, uint32_t& setCount /* @out */) const = 0;
virtual Core::hresult GetOptionalElements(std::vector<Core::OptionalType<uint32_t>>& values /* @out @restrict:0..255 */) const = 0;
virtual Core::hresult TransformOptionalElements(std::vector<Core::OptionalType<uint32_t>>& values /* @inout @restrict:0..255 */) const = 0;
Comment on lines +63 to +65

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 will be fixed


// ===== Optional vectors =====

virtual Core::hresult SummarizeOptionalVector(const Core::OptionalType<std::vector<uint32_t>>& values /* @in @restrict:0..255 */, bool& isSet /* @out */, uint32_t& valueSum /* @out */) const = 0;
virtual Core::hresult GetOptionalVector(Core::OptionalType<std::vector<uint32_t>>& values /* @out @restrict:0..255 */) const = 0;
virtual Core::hresult TransformOptionalVector(Core::OptionalType<std::vector<uint32_t>>& 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
1 change: 1 addition & 0 deletions tests/FunctionalTests/common/interfaces/Ids.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 10 additions & 4 deletions tests/FunctionalTests/common/interfaces/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<Struct>` 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.

Expand Down Expand Up @@ -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<T>` | ITestAsync, ITestOptionals, ITestEnums, ITestEvents, ITestRestrictions |
| `@optional` / `Core::OptionalType<T>` | ITestAsync, ITestOptionals, ITestVectors, ITestEnums, ITestEvents, ITestRestrictions |
| `@property` (read-write) | ITestEnums, ITestStructs, ITestAsync, ITestJsonUncompliantExtended, ITestJsonUncompliantCollapsed |
| `@property` (read-only) | ITestEnums, ITestAsync |
| `@property` (write-only) | ITestEnums |
Expand All @@ -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<Primitive>` | ITestStructs |
| `std::vector<Struct>` | ITestStructs, ITestEvents |
| `std::vector<Primitive>` | ITestStructs, ITestVectors |
| `std::vector<Struct>` | ITestStructs, ITestVectors, ITestEvents |
| `std::vector<std::vector<Primitive>>` | ITestVectors |
| `std::vector<Core::OptionalType<Primitive>>` | ITestVectors |
| `Core::OptionalType<std::vector<Primitive>>` | ITestOptionals, ITestVectors |
| `RPC::IStringIterator` | ITestIterators |
| `RPC::IValueIterator` | ITestIterators |

4 changes: 4 additions & 0 deletions tests/FunctionalTests/comrpc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Loading
Loading