From dac8a34e82c8186e94cd14ca4fe2c160fa44cc23 Mon Sep 17 00:00:00 2001 From: smanes0213 Date: Tue, 9 Jun 2026 14:41:21 +0530 Subject: [PATCH 01/29] Remove Mockshell dependency from JSONRPC Functional Tests --- .../workflows/ProxyStubFunctionalTests.yml | 3 +- tests/FunctionalTests/README.md | 6 +- .../external/thunder/CMakeLists.txt | 2 + tests/FunctionalTests/jsonrpc/CMakeLists.txt | 1 + .../FunctionalTests/jsonrpc/JsonRpcServer.cpp | 25 ++++- tests/FunctionalTests/jsonrpc/JsonRpcServer.h | 5 +- tests/FunctionalTests/jsonrpc/MockShell.h | 92 ------------------- 7 files changed, 32 insertions(+), 102 deletions(-) delete mode 100644 tests/FunctionalTests/jsonrpc/MockShell.h diff --git a/.github/workflows/ProxyStubFunctionalTests.yml b/.github/workflows/ProxyStubFunctionalTests.yml index 63f6b26d..4e497346 100644 --- a/.github/workflows/ProxyStubFunctionalTests.yml +++ b/.github/workflows/ProxyStubFunctionalTests.yml @@ -52,7 +52,7 @@ jobs: run: | set -euo pipefail export DEBIAN_FRONTEND=noninteractive - PKGS="python3-venv python3-pip build-essential cmake ninja-build" + PKGS="python3-venv python3-pip build-essential cmake ninja-build libgtest-dev" if [ "${{ matrix.architecture }}" = "32" ]; then PKGS="$PKGS zlib1g-dev:i386 libssl-dev:i386 gcc-13-multilib g++-13-multilib" else @@ -86,6 +86,7 @@ jobs: -DCMAKE_VERBOSE_MAKEFILE=ON \ -DENABLE_TESTING=ON \ -DFUNCTIONAL_TESTS=ON \ + -DENABLE_TEST_RUNTIME=ON \ -DPROXYSTUB_GENERATOR=$(pwd)/ProxyStubGenerator/StubGenerator.py \ -DJSON_GENERATOR=$(pwd)/JsonGenerator/JsonGenerator.py cmake \ diff --git a/tests/FunctionalTests/README.md b/tests/FunctionalTests/README.md index 8ad46a1e..85c54c80 100644 --- a/tests/FunctionalTests/README.md +++ b/tests/FunctionalTests/README.md @@ -158,8 +158,8 @@ The JSON-RPC test binary uses Thunder's `PluginHost::JSONRPC` for direct method │ │ │based registry) │ │ │ ↓ │ │ │ │ └─────────────────┘ │ │ JSON-RPC 2.0 │ │ │ │ │ │ params/response │ │ -│ │ MockShell │ │ │ │ -│ │ (IShell stub) │ │ │ │ +│ │ ThunderTestRuntime │ │ │ │ +│ │ + real IShell │ │ │ │ │ │ │ │ │ │ │ │ Impl (reused) │ │ │ │ │ └─────────────────────┘ └─────────────────┘ │ @@ -168,7 +168,7 @@ The JSON-RPC test binary uses Thunder's `PluginHost::JSONRPC` for direct method └────────────────────────────────────────────────────┘ ``` -**Server side** — `JsonRpcServer` inherits from `PluginHost::JSONRPC` and `PluginHost::IPlugin`. A `MockShell` provides the minimal `IShell` interface needed for initialization. Implementation classes are the same protocol-agnostic implementations used for COM-RPC tests. +**Server side** — `JsonRpcServer` inherits from `PluginHost::JSONRPC` and `PluginHost::IPlugin`. It initializes `Thunder::TestCore::ThunderTestRuntime` and attaches to a real `IShell` from the embedded Thunder `PluginHost::Server` (Controller callsign). Implementation classes are the same protocol-agnostic implementations used for COM-RPC tests. **Registration system** (header-only in `JsonRpcServer.h`): - `JsonRpcRegistrationProvider`: Singleton collecting registration lambdas diff --git a/tests/FunctionalTests/external/thunder/CMakeLists.txt b/tests/FunctionalTests/external/thunder/CMakeLists.txt index 79bbb34f..ac15ba62 100644 --- a/tests/FunctionalTests/external/thunder/CMakeLists.txt +++ b/tests/FunctionalTests/external/thunder/CMakeLists.txt @@ -39,10 +39,12 @@ if (ENABLE_JSON_RPC_TESTS) set(CRYPTALGO ON CACHE BOOL "" FORCE) set(PLUGINS ON CACHE BOOL "" FORCE) set(WEBSOCKET ON CACHE BOOL "" FORCE) + set(ENABLE_TEST_RUNTIME ON CACHE BOOL "" FORCE) else() set(CRYPTALGO OFF CACHE BOOL "" FORCE) set(PLUGINS OFF CACHE BOOL "" FORCE) set(WEBSOCKET OFF CACHE BOOL "" FORCE) + set(ENABLE_TEST_RUNTIME OFF CACHE BOOL "" FORCE) endif() FetchContent_MakeAvailable(Thunder) diff --git a/tests/FunctionalTests/jsonrpc/CMakeLists.txt b/tests/FunctionalTests/jsonrpc/CMakeLists.txt index d29f00bb..27ffefcf 100644 --- a/tests/FunctionalTests/jsonrpc/CMakeLists.txt +++ b/tests/FunctionalTests/jsonrpc/CMakeLists.txt @@ -43,6 +43,7 @@ endif() target_link_libraries(JsonRpcFunctionalTests PRIVATE + thunder_test_support ${NAMESPACE}Core::${NAMESPACE}Core ${NAMESPACE}COM::${NAMESPACE}COM ${NAMESPACE}WebSocket::${NAMESPACE}WebSocket diff --git a/tests/FunctionalTests/jsonrpc/JsonRpcServer.cpp b/tests/FunctionalTests/jsonrpc/JsonRpcServer.cpp index 524dbedc..e2636285 100644 --- a/tests/FunctionalTests/jsonrpc/JsonRpcServer.cpp +++ b/tests/FunctionalTests/jsonrpc/JsonRpcServer.cpp @@ -24,19 +24,36 @@ namespace JsonRpcServer { JsonRpcServer* g_server = nullptr; JsonRpcServer::JsonRpcServer() - : _mockShell("TestPlugin") { + const std::vector plugins; + const uint32_t result = _runtime.Initialize(plugins); + + ASSERT(result == Core::ERROR_NONE); + + _shell = _runtime.GetShell("Controller"); + ASSERT(_shell.IsValid() == true); + PluginHost::IShell::IConnectionServer::INotification* sink = nullptr; - Attach(sink, &_mockShell); - if (sink != nullptr) sink->Release(); + Attach(sink, _shell.operator->()); + if (sink != nullptr) { + sink->Release(); + } + Test::RegisterJsonRpcInterfaces(*this); } JsonRpcServer::~JsonRpcServer() { + Test::UnregisterJsonRpcInterfaces(*this); + PluginHost::IShell::IConnectionServer::INotification* sink = nullptr; Detach(sink); - if (sink != nullptr) sink->Release(); + if (sink != nullptr) { + sink->Release(); + } + + _shell.Release(); + _runtime.Deinitialize(); } } // namespace JsonRpcServer } // namespace Thunder \ No newline at end of file diff --git a/tests/FunctionalTests/jsonrpc/JsonRpcServer.h b/tests/FunctionalTests/jsonrpc/JsonRpcServer.h index f00c06c0..8070fbe5 100644 --- a/tests/FunctionalTests/jsonrpc/JsonRpcServer.h +++ b/tests/FunctionalTests/jsonrpc/JsonRpcServer.h @@ -20,8 +20,8 @@ #pragma once #include -#include "MockShell.h" #include "Module.h" +#include namespace Thunder { namespace JsonRpcServer { @@ -34,7 +34,8 @@ namespace JsonRpcServer { ~JsonRpcServer(); private: - MockShell _mockShell; + TestCore::ThunderTestRuntime _runtime; + Core::ProxyType _shell; }; extern JsonRpcServer* g_server; diff --git a/tests/FunctionalTests/jsonrpc/MockShell.h b/tests/FunctionalTests/jsonrpc/MockShell.h deleted file mode 100644 index d103bd09..00000000 --- a/tests/FunctionalTests/jsonrpc/MockShell.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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 "Module.h" - -namespace Thunder { -namespace JsonRpcServer { - class MockShell : public PluginHost::IShell { - public: - MockShell(const string& callsign) - : _callsign(callsign) - { - } - ~MockShell() override = default; - - // This object is owned by the JsonRpcServer and is not reference counted, so AddRef/Release do nothing. - uint32_t AddRef() const override { return Core::ERROR_COMPOSIT_OBJECT; } - uint32_t Release() const override { return Core::ERROR_COMPOSIT_OBJECT; } - - // Essential IShell methods - Callsign() is used by JSONRPC - string Callsign() const override { return _callsign; } - - // Stub implementations for other required virtual methods - void EnableWebServer(const string&, const string&) override { } - void DisableWebServer() override { } - string Model() const override { return string(); } - bool Background() const override { return false; } - string Accessor() const override { return string(); } - string WebPrefix() const override { return string(); } - string Locator() const override { return string(); } - string ClassName() const override { return string(); } - string PersistentPath() const override { return string(); } - string VolatilePath() const override { return string(); } - string DataPath() const override { return string(); } - string ProxyStubPath() const override { return string(); } - string SystemPath() const override { return string(); } - string PluginPath() const override { return string(); } - string SystemRootPath() const override { return string(); } - Core::hresult SystemRootPath(const string&) override { return Core::ERROR_NONE; } - startmode StartMode() const override { return startmode::DEACTIVATED; } - Core::hresult StartMode(const startmode) override { return Core::ERROR_NONE; } - string Substitute(const string&) const override { return string(); } - bool Resumed() const override { return false; } - Core::hresult Resumed(const bool) override { return Core::ERROR_NONE; } - string HashKey() const override { return string(); } - string ConfigLine() const override { return string(); } - Core::hresult ConfigLine(const string&) override { return Core::ERROR_NONE; } - Core::hresult Metadata(string&) const override { return Core::ERROR_NONE; } - PluginHost::ISubSystem* SubSystems() override { return nullptr; } - void Notify(const string&, const string&) override { } - void Register(PluginHost::IPlugin::INotification*, const Core::OptionalType& = { }) override { } - void Unregister(PluginHost::IPlugin::INotification*, const Core::OptionalType& = { }) override { } - state State() const override { return state::DEACTIVATED; } - void* QueryInterfaceByCallsign(const uint32_t, const string&) override { return nullptr; } - Core::hresult Activate(const reason) override { return Core::ERROR_NONE; } - Core::hresult Deactivate(const reason) override { return Core::ERROR_NONE; } - Core::hresult Unavailable(const reason) override { return Core::ERROR_NONE; } - Core::hresult Hibernate(const uint32_t) override { return Core::ERROR_NONE; } - reason Reason() const override { return reason::REQUESTED; } - uint32_t Submit(const uint32_t, const Core::ProxyType&) override { return Core::ERROR_NONE; } - RPC::IStringIterator* GetLibrarySearchPaths(const string&) const override { return nullptr; } - - void Register(PluginHost::IPlugin::INotification* sink, const uint32_t interface_id) override { } - void Unregister(PluginHost::IPlugin::INotification* sink, const uint32_t interface_id) override { } - - BEGIN_INTERFACE_MAP(MockShell) - INTERFACE_ENTRY(PluginHost::IShell) - END_INTERFACE_MAP - - private: - string _callsign; - }; -} // namespace JsonRpcServer -} // namespace Thunder From 88902cef6c3df2c80d31f6da146ce0cf67357bb2 Mon Sep 17 00:00:00 2001 From: smanes0213 Date: Wed, 10 Jun 2026 11:26:34 +0530 Subject: [PATCH 02/29] Resolve review comments --- tests/FunctionalTests/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/FunctionalTests/README.md b/tests/FunctionalTests/README.md index 85c54c80..e3bffa6c 100644 --- a/tests/FunctionalTests/README.md +++ b/tests/FunctionalTests/README.md @@ -140,7 +140,7 @@ ProxyStubs are generated by `ProxyStubGenerator` at build time into `build/comrp ## JSON-RPC test architecture -The JSON-RPC test binary uses Thunder's `PluginHost::JSONRPC` for direct method dispatch without network transport. The infrastructure is **header-only** - all registration and dispatch logic lives in `JsonRpcServer.h`: +The JSON-RPC test binary performs direct in-process dispatcher invocation (no network transport). `JsonRpcServer` is built on `Test::JsonRPCRegister` (from `JsonRpcRegistrations.h`), which derives from `PluginHost::JSONRPCSupportsEventStatus`. The infrastructure is **header-only** - all registration and dispatch logic lives in `JsonRpcServer.h`: ``` ┌────────────────────────────────────────────────────┐ @@ -168,7 +168,7 @@ The JSON-RPC test binary uses Thunder's `PluginHost::JSONRPC` for direct method └────────────────────────────────────────────────────┘ ``` -**Server side** — `JsonRpcServer` inherits from `PluginHost::JSONRPC` and `PluginHost::IPlugin`. It initializes `Thunder::TestCore::ThunderTestRuntime` and attaches to a real `IShell` from the embedded Thunder `PluginHost::Server` (Controller callsign). Implementation classes are the same protocol-agnostic implementations used for COM-RPC tests. +**Server side** — `JsonRpcServer` derives from `Test::JsonRPCRegister` (which derives from `PluginHost::JSONRPCSupportsEventStatus`) and does not implement `PluginHost::IPlugin`. It initializes `Thunder::TestCore::ThunderTestRuntime` and attaches to a real `IShell` from the embedded Thunder `PluginHost::Server` (Controller callsign). Implementation classes are the same protocol-agnostic implementations used for COM-RPC tests. **Registration system** (header-only in `JsonRpcServer.h`): - `JsonRpcRegistrationProvider`: Singleton collecting registration lambdas From 1d858df08759f75ecf19b4615034eb1b517a0a49 Mon Sep 17 00:00:00 2001 From: smanes0213 Date: Fri, 12 Jun 2026 14:35:16 +0530 Subject: [PATCH 03/29] Add new interface headers to add the missed flags and add respective test files --- .../common/interfaces/ITestEncodingMac.h | 57 +++++++++++ .../common/interfaces/ITestJsonFormatting.h | 96 +++++++++++++++++++ .../common/interfaces/ITestJsonShape.h | 67 +++++++++++++ .../common/interfaces/ITestLengthModes.h | 56 +++++++++++ tests/FunctionalTests/common/interfaces/Ids.h | 8 ++ .../comrpc/tests/TestEncodingMac.cpp | 50 ++++++++++ .../comrpc/tests/TestJsonFormatting.cpp | 63 ++++++++++++ .../comrpc/tests/TestJsonShape.cpp | 50 ++++++++++ .../comrpc/tests/TestLengthModes.cpp | 42 ++++++++ .../jsonrpc/tests/TestEncodingMacJsonRpc.cpp | 44 +++++++++ .../tests/TestJsonFormattingJsonRpc.cpp | 60 ++++++++++++ .../jsonrpc/tests/TestJsonShapeJsonRpc.cpp | 46 +++++++++ .../jsonrpc/tests/TestLengthModesJsonRpc.cpp | 40 ++++++++ 13 files changed, 679 insertions(+) create mode 100644 tests/FunctionalTests/common/interfaces/ITestEncodingMac.h create mode 100644 tests/FunctionalTests/common/interfaces/ITestJsonFormatting.h create mode 100644 tests/FunctionalTests/common/interfaces/ITestJsonShape.h create mode 100644 tests/FunctionalTests/common/interfaces/ITestLengthModes.h create mode 100644 tests/FunctionalTests/comrpc/tests/TestEncodingMac.cpp create mode 100644 tests/FunctionalTests/comrpc/tests/TestJsonFormatting.cpp create mode 100644 tests/FunctionalTests/comrpc/tests/TestJsonShape.cpp create mode 100644 tests/FunctionalTests/comrpc/tests/TestLengthModes.cpp create mode 100644 tests/FunctionalTests/jsonrpc/tests/TestEncodingMacJsonRpc.cpp create mode 100644 tests/FunctionalTests/jsonrpc/tests/TestJsonFormattingJsonRpc.cpp create mode 100644 tests/FunctionalTests/jsonrpc/tests/TestJsonShapeJsonRpc.cpp create mode 100644 tests/FunctionalTests/jsonrpc/tests/TestLengthModesJsonRpc.cpp diff --git a/tests/FunctionalTests/common/interfaces/ITestEncodingMac.h b/tests/FunctionalTests/common/interfaces/ITestEncodingMac.h new file mode 100644 index 00000000..f975b9a0 --- /dev/null +++ b/tests/FunctionalTests/common/interfaces/ITestEncodingMac.h @@ -0,0 +1,57 @@ +/* + * 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 { + + // Exercises @encode:mac behavior for fixed-size MAC address buffers. + // @json 1.0.0 + struct EXTERNAL ITestEncodingMac : virtual public Core::IUnknown { + enum { ID = ID_TEST_ENCODING_MAC }; + + // @brief Stores a 6-byte MAC address. + // In JSON-RPC, @encode:mac should map the byte array to a string + // representation (for example, "01:23:45:67:89:ab"). + // @param mac Input 6-byte MAC address. + // @retval ERROR_NONE Address stored. + virtual Core::hresult SetMacAddress( + const uint8_t mac[] /* @in @length:6 @encode:mac */) = 0; + + // @brief Retrieves the previously stored 6-byte MAC address. + // @param mac Receives stored 6-byte MAC address. + // @retval ERROR_NONE Address returned. + virtual Core::hresult GetMacAddress( + uint8_t mac[] /* @out @length:6 @maxlength:6 @encode:mac */) const = 0; + + // @brief Echoes a MAC address to verify round-trip conversion. + // @param input Input 6-byte MAC address. + // @param output Receives echoed 6-byte MAC address. + // @retval ERROR_NONE Echo completed. + virtual Core::hresult EchoMacAddress( + const uint8_t input[] /* @in @length:6 @encode:mac */, + uint8_t output[] /* @out @length:6 @maxlength:6 @encode:mac */) const = 0; + }; + +} // namespace FunctionalTest +} // namespace Thunder diff --git a/tests/FunctionalTests/common/interfaces/ITestJsonFormatting.h b/tests/FunctionalTests/common/interfaces/ITestJsonFormatting.h new file mode 100644 index 00000000..a8cf0532 --- /dev/null +++ b/tests/FunctionalTests/common/interfaces/ITestJsonFormatting.h @@ -0,0 +1,96 @@ +/* + * 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 { + + // @json 1.0.0 + // @text:keep + struct EXTERNAL ITestJsonTextKeep : virtual public Core::IUnknown { + enum { ID = ID_TEST_JSON_TEXT_KEEP }; + + // @brief Verifies that the original C++ identifier casing is preserved + // in generated JSON names when @text:keep is enabled. + // @param InputValue Input value used for round-trip verification. + // @param OutputValue Receives echoed value. + // @retval ERROR_NONE Echo completed. + virtual Core::hresult EchoMixedCaseName(const uint32_t InputValue /* @in */, uint32_t& OutputValue /* @out */) const = 0; + + // @property + // @brief Build version property. + // @param BuildVersion Receives current build version value. + // @retval ERROR_NONE Value returned. + virtual Core::hresult BuildVersion(uint32_t& BuildVersion /* @out */) const = 0; + }; + + // @json 1.0.0 + // @text:camelcase + struct EXTERNAL ITestJsonTextCase : virtual public Core::IUnknown { + enum { ID = ID_TEST_JSON_TEXT_CASE }; + + // @brief Verifies case-convention transformation in generated JSON names. + // @param sourceValue Input value for round-trip verification. + // @param resultValue Receives echoed value. + // @retval ERROR_NONE Echo completed. + virtual Core::hresult EchoCaseConvention(const uint16_t sourceValue /* @in */, uint16_t& resultValue /* @out */) const = 0; + }; + + // @json 1.0.0 + // @compliant + struct EXTERNAL ITestJsonCompliant : virtual public Core::IUnknown { + enum { ID = ID_TEST_JSON_COMPLIANT }; + + // @brief Baseline interface for compliant JSON-RPC format generation. + // @param payload Input payload. + // @param reply Receives output payload. + // @retval ERROR_NONE Operation completed. + virtual Core::hresult Ping(const string& payload /* @in */, string& reply /* @out */) const = 0; + }; + + // @json 1.0.0 + // @uncompliant:extended + struct EXTERNAL ITestJsonUncompliantExtended : virtual public Core::IUnknown { + enum { ID = ID_TEST_JSON_UNCOMPLIANT_EXT }; + + // @brief Interface-level tag target for extended uncompliant mode. + // @param payload Input payload. + // @param reply Receives output payload. + // @retval ERROR_NONE Operation completed. + virtual Core::hresult PingExtended(const string& payload /* @in */, string& reply /* @out */) const = 0; + }; + + // @json 1.0.0 + // @uncompliant:collapsed + struct EXTERNAL ITestJsonUncompliantCollapsed : virtual public Core::IUnknown { + enum { ID = ID_TEST_JSON_UNCOMPLIANT_COL }; + + // @brief Interface-level tag target for collapsed uncompliant mode. + // @param payload Input payload. + // @param reply Receives output payload. + // @retval ERROR_NONE Operation completed. + virtual Core::hresult PingCollapsed(const string& payload /* @in */, string& reply /* @out */) const = 0; + }; + +} // namespace FunctionalTest +} // namespace Thunder diff --git a/tests/FunctionalTests/common/interfaces/ITestJsonShape.h b/tests/FunctionalTests/common/interfaces/ITestJsonShape.h new file mode 100644 index 00000000..4973b0e5 --- /dev/null +++ b/tests/FunctionalTests/common/interfaces/ITestJsonShape.h @@ -0,0 +1,67 @@ +/* + * 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 { + + // Exercises JSON payload shape tags that are not covered by existing + // interfaces. Intended primarily for JsonGenerator functional tests. + // @json 1.0.0 + struct EXTERNAL ITestJsonShape : virtual public Core::IUnknown { + enum { ID = ID_TEST_JSON_SHAPE }; + + // @brief Returns a single scalar result using an object envelope even when + // one return value would otherwise be emitted without wrapping. + // @param counter Receives the current counter value. + // @retval ERROR_NONE Counter value returned. + // @wrapped + virtual Core::hresult GetWrappedCounter(uint32_t& counter /* @out */) const = 0; + + // @brief Echoes a list and requests single-element extraction in JSON. + // When the list has exactly one element, JSON may collapse it from + // [x] to x depending on the generator behavior. + // @param input Input list to echo. + // @param output Receives echoed list. + // @retval ERROR_NONE Echo completed. + virtual Core::hresult EchoExtractedList( + const std::vector& input /* @in @extract */, + std::vector& output /* @out @extract */) const = 0; + + // @brief Echoes a struct and requests collapsed representation in JSON. + struct Dimensions { + uint16_t width; + uint16_t height; + }; + + // @brief Echoes a struct and requests collapsed representation in JSON. + // @param in Input Dimensions value. + // @param out Receives echoed Dimensions value. + // @retval ERROR_NONE Echo completed. + virtual Core::hresult EchoExtractedStruct( + const Dimensions& in /* @in @extract */, + Dimensions& out /* @out @extract */) const = 0; + }; + +} // namespace FunctionalTest +} // namespace Thunder diff --git a/tests/FunctionalTests/common/interfaces/ITestLengthModes.h b/tests/FunctionalTests/common/interfaces/ITestLengthModes.h new file mode 100644 index 00000000..752fde25 --- /dev/null +++ b/tests/FunctionalTests/common/interfaces/ITestLengthModes.h @@ -0,0 +1,56 @@ +/* + * 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 { + + // Exercises less-common @length modes that are not covered in existing + // functional interfaces. + // @json 1.0.0 + struct EXTERNAL ITestLengthModes : virtual public Core::IUnknown { + enum { ID = ID_TEST_LENGTH_MODES }; + + // @brief Echoes exactly one byte using implicit one-element sizing. + // The @length:void annotation indicates that the buffer length is + // implied by element size. + // @param input Input single-byte payload. + // @param output Receives echoed single-byte payload. + // @retval ERROR_NONE Echo completed. + virtual Core::hresult EchoSingleByte( + const uint8_t input[] /* @in @length:void */, + uint8_t output[] /* @out @length:void @maxlength:1 */) const = 0; + + // @brief Fills an output buffer and returns the payload length directly. + // Intended to cover the @length:return annotation in a signature + // where return type carries the produced byte count. + // @param output Receives payload bytes. + // @param maxSize Maximum writable bytes in output. + // @retval Payload byte count written to output. + virtual uint16_t ReadPayload( + uint8_t output[] /* @out @length:return @maxlength:maxSize */, + const uint16_t maxSize /* @in */) const = 0; + }; + +} // namespace FunctionalTest +} // namespace Thunder diff --git a/tests/FunctionalTests/common/interfaces/Ids.h b/tests/FunctionalTests/common/interfaces/Ids.h index 5f120656..28e02add 100644 --- a/tests/FunctionalTests/common/interfaces/Ids.h +++ b/tests/FunctionalTests/common/interfaces/Ids.h @@ -39,6 +39,14 @@ namespace Thunder { ID_TEST_RESTRICTIONS = ID_INTERFACE_OFFSET + 0x00A, ID_TEST_ASYNC = ID_INTERFACE_OFFSET + 0x00B, ID_TEST_ASYNC_CALLBACK = ID_INTERFACE_OFFSET + 0x00C, + ID_TEST_JSON_SHAPE = ID_INTERFACE_OFFSET + 0x00D, + ID_TEST_JSON_TEXT_KEEP = ID_INTERFACE_OFFSET + 0x00E, + ID_TEST_JSON_TEXT_CASE = ID_INTERFACE_OFFSET + 0x00F, + ID_TEST_JSON_COMPLIANT = ID_INTERFACE_OFFSET + 0x010, + ID_TEST_JSON_UNCOMPLIANT_EXT = ID_INTERFACE_OFFSET + 0x011, + ID_TEST_JSON_UNCOMPLIANT_COL = ID_INTERFACE_OFFSET + 0x012, + ID_TEST_ENCODING_MAC = ID_INTERFACE_OFFSET + 0x013, + ID_TEST_LENGTH_MODES = ID_INTERFACE_OFFSET + 0x014, }; } // namespace FunctionalTest diff --git a/tests/FunctionalTests/comrpc/tests/TestEncodingMac.cpp b/tests/FunctionalTests/comrpc/tests/TestEncodingMac.cpp new file mode 100644 index 00000000..014177cf --- /dev/null +++ b/tests/FunctionalTests/comrpc/tests/TestEncodingMac.cpp @@ -0,0 +1,50 @@ +/* + * 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 TestEncodingMac : public Testing::TestHarness {}; + +TEST_F(TestEncodingMac, SetGetMacAddress) { + const uint8_t input[6] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB }; + ASSERT_EQ(_proxy->SetMacAddress(input), Core::ERROR_NONE); + + uint8_t output[6] = { 0 }; + ASSERT_EQ(_proxy->GetMacAddress(output), Core::ERROR_NONE); + + for (uint8_t i = 0; i < 6; ++i) { + EXPECT_EQ(output[i], input[i]); + } +} + +TEST_F(TestEncodingMac, EchoMacAddress) { + const uint8_t input[6] = { 0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x01 }; + uint8_t output[6] = { 0 }; + + ASSERT_EQ(_proxy->EchoMacAddress(input, output), Core::ERROR_NONE); + + for (uint8_t i = 0; i < 6; ++i) { + EXPECT_EQ(output[i], input[i]); + } +} diff --git a/tests/FunctionalTests/comrpc/tests/TestJsonFormatting.cpp b/tests/FunctionalTests/comrpc/tests/TestJsonFormatting.cpp new file mode 100644 index 00000000..cac0902a --- /dev/null +++ b/tests/FunctionalTests/comrpc/tests/TestJsonFormatting.cpp @@ -0,0 +1,63 @@ +/* + * 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 TestJsonTextKeep : public Testing::TestHarness {}; +class TestJsonTextCase : public Testing::TestHarness {}; +class TestJsonCompliant : public Testing::TestHarness {}; +class TestJsonUncompliantExtended : public Testing::TestHarness {}; +class TestJsonUncompliantCollapsed : public Testing::TestHarness {}; + +TEST_F(TestJsonTextKeep, EchoMixedCaseName) { + uint32_t output = 0; + ASSERT_EQ(_proxy->EchoMixedCaseName(77, output), Core::ERROR_NONE); + EXPECT_EQ(output, 77u); +} + +TEST_F(TestJsonTextKeep, BuildVersionProperty) { + uint32_t version = 0; + ASSERT_EQ(_proxy->BuildVersion(version), Core::ERROR_NONE); +} + +TEST_F(TestJsonTextCase, EchoCaseConvention) { + uint16_t result = 0; + ASSERT_EQ(_proxy->EchoCaseConvention(123, result), Core::ERROR_NONE); + EXPECT_EQ(result, 123u); +} + +TEST_F(TestJsonCompliant, Ping) { + string reply; + ASSERT_EQ(_proxy->Ping("payload", reply), Core::ERROR_NONE); +} + +TEST_F(TestJsonUncompliantExtended, PingExtended) { + string reply; + ASSERT_EQ(_proxy->PingExtended("payload", reply), Core::ERROR_NONE); +} + +TEST_F(TestJsonUncompliantCollapsed, PingCollapsed) { + string reply; + ASSERT_EQ(_proxy->PingCollapsed("payload", reply), Core::ERROR_NONE); +} diff --git a/tests/FunctionalTests/comrpc/tests/TestJsonShape.cpp b/tests/FunctionalTests/comrpc/tests/TestJsonShape.cpp new file mode 100644 index 00000000..38930937 --- /dev/null +++ b/tests/FunctionalTests/comrpc/tests/TestJsonShape.cpp @@ -0,0 +1,50 @@ +/* + * 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 TestJsonShape : public Testing::TestHarness {}; + +TEST_F(TestJsonShape, GetWrappedCounter) { + uint32_t counter = 0; + ASSERT_EQ(_proxy->GetWrappedCounter(counter), Core::ERROR_NONE); +} + +TEST_F(TestJsonShape, EchoExtractedList) { + const std::vector input { 7 }; + std::vector output; + + ASSERT_EQ(_proxy->EchoExtractedList(input, output), Core::ERROR_NONE); + ASSERT_EQ(output.size(), input.size()); + EXPECT_EQ(output[0], input[0]); +} + +TEST_F(TestJsonShape, EchoExtractedStruct) { + const ITestJsonShape::Dimensions input { 1920, 1080 }; + ITestJsonShape::Dimensions output { 0, 0 }; + + ASSERT_EQ(_proxy->EchoExtractedStruct(input, output), Core::ERROR_NONE); + EXPECT_EQ(output.width, input.width); + EXPECT_EQ(output.height, input.height); +} diff --git a/tests/FunctionalTests/comrpc/tests/TestLengthModes.cpp b/tests/FunctionalTests/comrpc/tests/TestLengthModes.cpp new file mode 100644 index 00000000..d6ccd082 --- /dev/null +++ b/tests/FunctionalTests/comrpc/tests/TestLengthModes.cpp @@ -0,0 +1,42 @@ +/* + * 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 TestLengthModes : public Testing::TestHarness {}; + +TEST_F(TestLengthModes, EchoSingleByte) { + const uint8_t input[1] = { 0x5A }; + uint8_t output[1] = { 0 }; + + ASSERT_EQ(_proxy->EchoSingleByte(input, output), Core::ERROR_NONE); + EXPECT_EQ(output[0], input[0]); +} + +TEST_F(TestLengthModes, ReadPayloadHonorsCapacity) { + uint8_t output[16] = { 0 }; + + const uint16_t written = _proxy->ReadPayload(output, static_cast(sizeof(output))); + EXPECT_LE(written, static_cast(sizeof(output))); +} diff --git a/tests/FunctionalTests/jsonrpc/tests/TestEncodingMacJsonRpc.cpp b/tests/FunctionalTests/jsonrpc/tests/TestEncodingMacJsonRpc.cpp new file mode 100644 index 00000000..3259ebeb --- /dev/null +++ b/tests/FunctionalTests/jsonrpc/tests/TestEncodingMacJsonRpc.cpp @@ -0,0 +1,44 @@ +/* + * 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 "JsonRpcTestHarness.h" +#include + +using namespace Thunder; + +class TestEncodingMacJsonRpc : public JsonRpcTesting::JsonRpcTestHarness {}; + +TEST_F(TestEncodingMacJsonRpc, SetGetMacAddress) { + string response; + + EXPECT_EQ(Core::ERROR_NONE, + CallMethod("setMacAddress", R"({"mac":"01:23:45:67:89:ab"})", response)); + + response.clear(); + EXPECT_EQ(Core::ERROR_NONE, CallMethod("getMacAddress", "{}", response)); + EXPECT_FALSE(response.empty()); +} + +TEST_F(TestEncodingMacJsonRpc, EchoMacAddress) { + string response; + EXPECT_EQ(Core::ERROR_NONE, + CallMethod("echoMacAddress", R"({"input":"de:ad:be:ef:00:01"})", response)); + EXPECT_FALSE(response.empty()); +} diff --git a/tests/FunctionalTests/jsonrpc/tests/TestJsonFormattingJsonRpc.cpp b/tests/FunctionalTests/jsonrpc/tests/TestJsonFormattingJsonRpc.cpp new file mode 100644 index 00000000..ab9f8069 --- /dev/null +++ b/tests/FunctionalTests/jsonrpc/tests/TestJsonFormattingJsonRpc.cpp @@ -0,0 +1,60 @@ +/* + * 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 "JsonRpcTestHarness.h" +#include + +using namespace Thunder; + +class TestJsonFormattingJsonRpc : public JsonRpcTesting::JsonRpcTestHarness {}; + +TEST_F(TestJsonFormattingJsonRpc, PingCompliant) { + string response; + EXPECT_EQ(Core::ERROR_NONE, CallMethod("ping", R"({"payload":"abc"})", response)); + EXPECT_FALSE(response.empty()); +} + +TEST_F(TestJsonFormattingJsonRpc, DISABLED_TextKeepNaming) { + string response; + EXPECT_EQ(Core::ERROR_NONE, + CallMethod("echoMixedCaseName", R"({"InputValue":77})", response)); + EXPECT_FALSE(response.empty()); +} + +TEST_F(TestJsonFormattingJsonRpc, DISABLED_TextCaseNaming) { + string response; + EXPECT_EQ(Core::ERROR_NONE, + CallMethod("echoCaseConvention", R"({"sourceValue":123})", response)); + EXPECT_FALSE(response.empty()); +} + +TEST_F(TestJsonFormattingJsonRpc, DISABLED_UncompliantExtendedShape) { + string response; + EXPECT_EQ(Core::ERROR_NONE, + CallMethod("pingExtended", R"({"payload":"abc"})", response)); + EXPECT_FALSE(response.empty()); +} + +TEST_F(TestJsonFormattingJsonRpc, DISABLED_UncompliantCollapsedShape) { + string response; + EXPECT_EQ(Core::ERROR_NONE, + CallMethod("pingCollapsed", R"({"payload":"abc"})", response)); + EXPECT_FALSE(response.empty()); +} diff --git a/tests/FunctionalTests/jsonrpc/tests/TestJsonShapeJsonRpc.cpp b/tests/FunctionalTests/jsonrpc/tests/TestJsonShapeJsonRpc.cpp new file mode 100644 index 00000000..efa87103 --- /dev/null +++ b/tests/FunctionalTests/jsonrpc/tests/TestJsonShapeJsonRpc.cpp @@ -0,0 +1,46 @@ +/* + * 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 "JsonRpcTestHarness.h" +#include + +using namespace Thunder; + +class TestJsonShapeJsonRpc : public JsonRpcTesting::JsonRpcTestHarness {}; + +TEST_F(TestJsonShapeJsonRpc, GetWrappedCounter) { + string response; + EXPECT_EQ(Core::ERROR_NONE, CallMethod("getWrappedCounter", "{}", response)); + EXPECT_FALSE(response.empty()); +} + +TEST_F(TestJsonShapeJsonRpc, EchoExtractedList) { + string response; + EXPECT_EQ(Core::ERROR_NONE, + CallMethod("echoExtractedList", R"({"input":[7]})", response)); + EXPECT_FALSE(response.empty()); +} + +TEST_F(TestJsonShapeJsonRpc, EchoExtractedStruct) { + string response; + EXPECT_EQ(Core::ERROR_NONE, + CallMethod("echoExtractedStruct", R"({"in":{"width":1920,"height":1080}})", response)); + EXPECT_FALSE(response.empty()); +} diff --git a/tests/FunctionalTests/jsonrpc/tests/TestLengthModesJsonRpc.cpp b/tests/FunctionalTests/jsonrpc/tests/TestLengthModesJsonRpc.cpp new file mode 100644 index 00000000..bb8c46a7 --- /dev/null +++ b/tests/FunctionalTests/jsonrpc/tests/TestLengthModesJsonRpc.cpp @@ -0,0 +1,40 @@ +/* + * 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 "JsonRpcTestHarness.h" +#include + +using namespace Thunder; + +class TestLengthModesJsonRpc : public JsonRpcTesting::JsonRpcTestHarness {}; + +TEST_F(TestLengthModesJsonRpc, EchoSingleByte) { + string response; + EXPECT_EQ(Core::ERROR_NONE, + CallMethod("echoSingleByte", R"({"input":"Wg=="})", response)); + EXPECT_FALSE(response.empty()); +} + +TEST_F(TestLengthModesJsonRpc, ReadPayload) { + string response; + EXPECT_EQ(Core::ERROR_NONE, + CallMethod("readPayload", R"({"maxSize":16})", response)); + EXPECT_FALSE(response.empty()); +} From 40a31e355abdf32b891657683157cee53ed295a0 Mon Sep 17 00:00:00 2001 From: smanes0213 Date: Mon, 15 Jun 2026 16:44:30 +0530 Subject: [PATCH 04/29] Add implementation files and update CMake accordingly --- tests/FunctionalTests/CMakeLists.txt | 4 + tests/FunctionalTests/common/CMakeLists.txt | 46 ++++++ .../implementations/TestEncodingMacImpl.cpp | 64 ++++++++ .../TestJsonFormattingImpl.cpp | 147 ++++++++++++++++++ .../implementations/TestJsonShapeImpl.cpp | 67 ++++++++ .../implementations/TestLengthModesImpl.cpp | 60 +++++++ .../common/interfaces/ITestJsonFormatting.h | 2 +- .../common/interfaces/ITestJsonShape.h | 8 +- .../common/interfaces/ITestLengthModes.h | 15 +- tests/FunctionalTests/comrpc/CMakeLists.txt | 24 ++- .../comrpc/tests/TestLengthModes.cpp | 4 +- tests/FunctionalTests/jsonrpc/CMakeLists.txt | 14 ++ .../tests/TestJsonFormattingJsonRpc.cpp | 14 +- 13 files changed, 443 insertions(+), 26 deletions(-) create mode 100644 tests/FunctionalTests/common/implementations/TestEncodingMacImpl.cpp create mode 100644 tests/FunctionalTests/common/implementations/TestJsonFormattingImpl.cpp create mode 100644 tests/FunctionalTests/common/implementations/TestJsonShapeImpl.cpp create mode 100644 tests/FunctionalTests/common/implementations/TestLengthModesImpl.cpp diff --git a/tests/FunctionalTests/CMakeLists.txt b/tests/FunctionalTests/CMakeLists.txt index 00686313..61809531 100644 --- a/tests/FunctionalTests/CMakeLists.txt +++ b/tests/FunctionalTests/CMakeLists.txt @@ -25,6 +25,10 @@ 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_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) +option(TEST_JSON_FORMATTING "Enable @text/@compliant/@uncompliant tests" ON) set(CMAKE_MODULE_PATH "${CMAKE_BINARY_DIR}" CACHE BOOL "" FORCE) diff --git a/tests/FunctionalTests/common/CMakeLists.txt b/tests/FunctionalTests/common/CMakeLists.txt index 1e73f51b..8a12f0ce 100644 --- a/tests/FunctionalTests/common/CMakeLists.txt +++ b/tests/FunctionalTests/common/CMakeLists.txt @@ -155,6 +155,52 @@ if (TEST_ASYNC) AddTestInterface("Async" COM_RPC JSON_RPC) endif() +if (TEST_ENCODING_MAC) + AddTestInterface("EncodingMac" COM_RPC JSON_RPC) +endif() + +if (TEST_LENGTH_MODES) + AddTestInterface("LengthModes" COM_RPC) # COM-RPC only — @length:return requires non-uint32_t return type, unsupported by JsonGenerator +endif() + +if (TEST_JSON_SHAPE) + AddTestInterface("JsonShape" COM_RPC JSON_RPC) +endif() + +if (TEST_JSON_FORMATTING) + # Multiple @json interfaces in one header — register each sub-interface individually + # COM-RPC ProxyStubs are also needed for the COM-RPC test harness + target_sources(FunctionalTestCommon PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/implementations/TestJsonFormattingImpl.cpp" + ) + list(APPEND COM_RPC_INTERFACE_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/interfaces/ITestJsonFormatting.h") + list(APPEND JSON_INTERFACE_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/interfaces/ITestJsonFormatting.h") + string(APPEND JSON_RPC_REGISTRATIONS + " FunctionalTest::JTestJsonTextKeep::Register(module,\n" + " static_cast(\n" + " TestImplementation::Factory::Instance().Create(FunctionalTest::ITestJsonTextKeep::ID)));\n\n" + " FunctionalTest::JTestJsonTextCase::Register(module,\n" + " static_cast(\n" + " TestImplementation::Factory::Instance().Create(FunctionalTest::ITestJsonTextCase::ID)));\n\n" + " FunctionalTest::JTestJsonCompliant::Register(module,\n" + " static_cast(\n" + " TestImplementation::Factory::Instance().Create(FunctionalTest::ITestJsonCompliant::ID)));\n\n" + " FunctionalTest::JTestJsonUncompliantExtended::Register(module,\n" + " static_cast(\n" + " TestImplementation::Factory::Instance().Create(FunctionalTest::ITestJsonUncompliantExtended::ID)));\n\n" + " FunctionalTest::JTestJsonUncompliantCollapsed::Register(module,\n" + " static_cast(\n" + " TestImplementation::Factory::Instance().Create(FunctionalTest::ITestJsonUncompliantCollapsed::ID)));\n\n" + ) + string(APPEND JSON_RPC_UNREGISTRATIONS + " FunctionalTest::JTestJsonTextKeep::Unregister(module);\n" + " FunctionalTest::JTestJsonTextCase::Unregister(module);\n" + " FunctionalTest::JTestJsonCompliant::Unregister(module);\n" + " FunctionalTest::JTestJsonUncompliantExtended::Unregister(module);\n" + " FunctionalTest::JTestJsonUncompliantCollapsed::Unregister(module);\n" + ) +endif() + list(LENGTH COM_RPC_INTERFACE_HEADERS NUM_COM_TESTS) list(LENGTH JSON_INTERFACE_HEADERS NUM_JSON_TESTS) diff --git a/tests/FunctionalTests/common/implementations/TestEncodingMacImpl.cpp b/tests/FunctionalTests/common/implementations/TestEncodingMacImpl.cpp new file mode 100644 index 00000000..65add63c --- /dev/null +++ b/tests/FunctionalTests/common/implementations/TestEncodingMacImpl.cpp @@ -0,0 +1,64 @@ +/* + * 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 { + + class TestEncodingMacImpl : public FunctionalTest::ITestEncodingMac { + public: + TestEncodingMacImpl() { memset(_mac, 0, sizeof(_mac)); } + ~TestEncodingMacImpl() override = default; + + TestEncodingMacImpl(const TestEncodingMacImpl&) = delete; + TestEncodingMacImpl& operator=(const TestEncodingMacImpl&) = delete; + + Core::hresult SetMacAddress(const uint8_t mac[]) override + { + memcpy(_mac, mac, 6); + return Core::ERROR_NONE; + } + + Core::hresult GetMacAddress(uint8_t mac[]) const override + { + memcpy(mac, _mac, 6); + return Core::ERROR_NONE; + } + + Core::hresult EchoMacAddress(const uint8_t input[], uint8_t output[]) const override + { + memcpy(output, input, 6); + return Core::ERROR_NONE; + } + + BEGIN_INTERFACE_MAP(TestEncodingMacImpl) + INTERFACE_ENTRY(FunctionalTest::ITestEncodingMac) + END_INTERFACE_MAP + + private: + uint8_t _mac[6]; + }; + + static Factory::Registrar g_encodingMacRegistrar; + +} // namespace TestImplementation +} // namespace Thunder diff --git a/tests/FunctionalTests/common/implementations/TestJsonFormattingImpl.cpp b/tests/FunctionalTests/common/implementations/TestJsonFormattingImpl.cpp new file mode 100644 index 00000000..7185dc27 --- /dev/null +++ b/tests/FunctionalTests/common/implementations/TestJsonFormattingImpl.cpp @@ -0,0 +1,147 @@ +/* + * 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 + +namespace Thunder { +namespace TestImplementation { + + // ===== ITestJsonTextKeep ===== + + class TestJsonTextKeepImpl : public FunctionalTest::ITestJsonTextKeep { + public: + TestJsonTextKeepImpl() : _buildVersion(1) {} + ~TestJsonTextKeepImpl() override = default; + + TestJsonTextKeepImpl(const TestJsonTextKeepImpl&) = delete; + TestJsonTextKeepImpl& operator=(const TestJsonTextKeepImpl&) = delete; + + Core::hresult EchoMixedCaseName(const uint32_t InputValue, uint32_t& OutputValue) const override + { + OutputValue = InputValue; + return Core::ERROR_NONE; + } + + Core::hresult BuildVersion(uint32_t& BuildVersion) const override + { + BuildVersion = _buildVersion; + return Core::ERROR_NONE; + } + + BEGIN_INTERFACE_MAP(TestJsonTextKeepImpl) + INTERFACE_ENTRY(FunctionalTest::ITestJsonTextKeep) + END_INTERFACE_MAP + + private: + uint32_t _buildVersion; + }; + + // ===== ITestJsonTextCase ===== + + class TestJsonTextCaseImpl : public FunctionalTest::ITestJsonTextCase { + public: + TestJsonTextCaseImpl() = default; + ~TestJsonTextCaseImpl() override = default; + + TestJsonTextCaseImpl(const TestJsonTextCaseImpl&) = delete; + TestJsonTextCaseImpl& operator=(const TestJsonTextCaseImpl&) = delete; + + Core::hresult EchoCaseConvention(const uint16_t sourceValue, uint16_t& resultValue) const override + { + resultValue = sourceValue; + return Core::ERROR_NONE; + } + + BEGIN_INTERFACE_MAP(TestJsonTextCaseImpl) + INTERFACE_ENTRY(FunctionalTest::ITestJsonTextCase) + END_INTERFACE_MAP + }; + + // ===== ITestJsonCompliant ===== + + class TestJsonCompliantImpl : public FunctionalTest::ITestJsonCompliant { + public: + TestJsonCompliantImpl() = default; + ~TestJsonCompliantImpl() override = default; + + TestJsonCompliantImpl(const TestJsonCompliantImpl&) = delete; + TestJsonCompliantImpl& operator=(const TestJsonCompliantImpl&) = delete; + + Core::hresult Ping(const string& payload, string& reply) const override + { + reply = payload; + return Core::ERROR_NONE; + } + + BEGIN_INTERFACE_MAP(TestJsonCompliantImpl) + INTERFACE_ENTRY(FunctionalTest::ITestJsonCompliant) + END_INTERFACE_MAP + }; + + // ===== ITestJsonUncompliantExtended ===== + + class TestJsonUncompliantExtendedImpl : public FunctionalTest::ITestJsonUncompliantExtended { + public: + TestJsonUncompliantExtendedImpl() = default; + ~TestJsonUncompliantExtendedImpl() override = default; + + TestJsonUncompliantExtendedImpl(const TestJsonUncompliantExtendedImpl&) = delete; + TestJsonUncompliantExtendedImpl& operator=(const TestJsonUncompliantExtendedImpl&) = delete; + + Core::hresult PingExtended(const string& payload, string& reply) const override + { + reply = payload; + return Core::ERROR_NONE; + } + + BEGIN_INTERFACE_MAP(TestJsonUncompliantExtendedImpl) + INTERFACE_ENTRY(FunctionalTest::ITestJsonUncompliantExtended) + END_INTERFACE_MAP + }; + + // ===== ITestJsonUncompliantCollapsed ===== + + class TestJsonUncompliantCollapsedImpl : public FunctionalTest::ITestJsonUncompliantCollapsed { + public: + TestJsonUncompliantCollapsedImpl() = default; + ~TestJsonUncompliantCollapsedImpl() override = default; + + TestJsonUncompliantCollapsedImpl(const TestJsonUncompliantCollapsedImpl&) = delete; + TestJsonUncompliantCollapsedImpl& operator=(const TestJsonUncompliantCollapsedImpl&) = delete; + + Core::hresult PingCollapsed(const string& payload, string& reply) const override + { + reply = payload; + return Core::ERROR_NONE; + } + + BEGIN_INTERFACE_MAP(TestJsonUncompliantCollapsedImpl) + INTERFACE_ENTRY(FunctionalTest::ITestJsonUncompliantCollapsed) + END_INTERFACE_MAP + }; + + static Factory::Registrar g_jsonTextKeepRegistrar; + static Factory::Registrar g_jsonTextCaseRegistrar; + static Factory::Registrar g_jsonCompliantRegistrar; + static Factory::Registrar g_jsonUncompliantExtRegistrar; + static Factory::Registrar g_jsonUncompliantColRegistrar; + +} // namespace TestImplementation +} // namespace Thunder diff --git a/tests/FunctionalTests/common/implementations/TestJsonShapeImpl.cpp b/tests/FunctionalTests/common/implementations/TestJsonShapeImpl.cpp new file mode 100644 index 00000000..737faee8 --- /dev/null +++ b/tests/FunctionalTests/common/implementations/TestJsonShapeImpl.cpp @@ -0,0 +1,67 @@ +/* + * 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 + +namespace Thunder { +namespace TestImplementation { + + class TestJsonShapeImpl : public FunctionalTest::ITestJsonShape { + public: + TestJsonShapeImpl() : _counter(42) {} + ~TestJsonShapeImpl() override = default; + + TestJsonShapeImpl(const TestJsonShapeImpl&) = delete; + TestJsonShapeImpl& operator=(const TestJsonShapeImpl&) = delete; + + Core::hresult GetWrappedCounter(uint32_t& counter) const override + { + counter = _counter; + return Core::ERROR_NONE; + } + + Core::hresult EchoExtractedList( + const std::vector& input, + std::vector& output) const override + { + output = input; + return Core::ERROR_NONE; + } + + Core::hresult EchoExtractedStruct( + const Dimensions& in, + Dimensions& out) const override + { + out = in; + return Core::ERROR_NONE; + } + + BEGIN_INTERFACE_MAP(TestJsonShapeImpl) + INTERFACE_ENTRY(FunctionalTest::ITestJsonShape) + END_INTERFACE_MAP + + private: + uint32_t _counter; + }; + + static Factory::Registrar g_jsonShapeRegistrar; + +} // namespace TestImplementation +} // namespace Thunder diff --git a/tests/FunctionalTests/common/implementations/TestLengthModesImpl.cpp b/tests/FunctionalTests/common/implementations/TestLengthModesImpl.cpp new file mode 100644 index 00000000..6af1e7b2 --- /dev/null +++ b/tests/FunctionalTests/common/implementations/TestLengthModesImpl.cpp @@ -0,0 +1,60 @@ +/* + * 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 { + + class TestLengthModesImpl : public FunctionalTest::ITestLengthModes { + public: + TestLengthModesImpl() = default; + ~TestLengthModesImpl() override = default; + + TestLengthModesImpl(const TestLengthModesImpl&) = delete; + TestLengthModesImpl& operator=(const TestLengthModesImpl&) = delete; + + Core::hresult EchoSingleByte(const uint8_t input, uint8_t output[]) const override + { + output[0] = input; + return Core::ERROR_NONE; + } + + uint16_t ReadPayload(uint8_t output[], const uint16_t maxSize) const override + { + static const uint8_t pattern[] = { 0xDE, 0xAD, 0xBE, 0xEF }; + static const uint16_t patternSize = static_cast(sizeof(pattern)); + const uint16_t count = std::min(maxSize, patternSize); + for (uint16_t i = 0; i < count; ++i) { + output[i] = pattern[i]; + } + return count; + } + + BEGIN_INTERFACE_MAP(TestLengthModesImpl) + INTERFACE_ENTRY(FunctionalTest::ITestLengthModes) + END_INTERFACE_MAP + }; + + static Factory::Registrar g_lengthModesRegistrar; + +} // namespace TestImplementation +} // namespace Thunder diff --git a/tests/FunctionalTests/common/interfaces/ITestJsonFormatting.h b/tests/FunctionalTests/common/interfaces/ITestJsonFormatting.h index a8cf0532..8df105a3 100644 --- a/tests/FunctionalTests/common/interfaces/ITestJsonFormatting.h +++ b/tests/FunctionalTests/common/interfaces/ITestJsonFormatting.h @@ -45,7 +45,7 @@ namespace FunctionalTest { }; // @json 1.0.0 - // @text:camelcase + // @text:legacy struct EXTERNAL ITestJsonTextCase : virtual public Core::IUnknown { enum { ID = ID_TEST_JSON_TEXT_CASE }; diff --git a/tests/FunctionalTests/common/interfaces/ITestJsonShape.h b/tests/FunctionalTests/common/interfaces/ITestJsonShape.h index 4973b0e5..defb64a8 100644 --- a/tests/FunctionalTests/common/interfaces/ITestJsonShape.h +++ b/tests/FunctionalTests/common/interfaces/ITestJsonShape.h @@ -45,8 +45,8 @@ namespace FunctionalTest { // @param output Receives echoed list. // @retval ERROR_NONE Echo completed. virtual Core::hresult EchoExtractedList( - const std::vector& input /* @in @extract */, - std::vector& output /* @out @extract */) const = 0; + const std::vector& input /* @in @extract @restrict:1..64 */, + std::vector& output /* @out @extract @restrict:1..64 */) const = 0; // @brief Echoes a struct and requests collapsed representation in JSON. struct Dimensions { @@ -59,8 +59,8 @@ namespace FunctionalTest { // @param out Receives echoed Dimensions value. // @retval ERROR_NONE Echo completed. virtual Core::hresult EchoExtractedStruct( - const Dimensions& in /* @in @extract */, - Dimensions& out /* @out @extract */) const = 0; + const Dimensions& in /* @in */, + Dimensions& out /* @out */) const = 0; }; } // namespace FunctionalTest diff --git a/tests/FunctionalTests/common/interfaces/ITestLengthModes.h b/tests/FunctionalTests/common/interfaces/ITestLengthModes.h index 752fde25..1ec3b76d 100644 --- a/tests/FunctionalTests/common/interfaces/ITestLengthModes.h +++ b/tests/FunctionalTests/common/interfaces/ITestLengthModes.h @@ -31,18 +31,17 @@ namespace FunctionalTest { struct EXTERNAL ITestLengthModes : virtual public Core::IUnknown { enum { ID = ID_TEST_LENGTH_MODES }; - // @brief Echoes exactly one byte using implicit one-element sizing. - // The @length:void annotation indicates that the buffer length is - // implied by element size. - // @param input Input single-byte payload. - // @param output Receives echoed single-byte payload. + // @brief Echoes exactly one byte, exercising the length:1 output annotation + // which signals that the output buffer holds exactly one element. + // @param input Input single-byte value. + // @param output Receives the echoed byte. // @retval ERROR_NONE Echo completed. virtual Core::hresult EchoSingleByte( - const uint8_t input[] /* @in @length:void */, - uint8_t output[] /* @out @length:void @maxlength:1 */) const = 0; + const uint8_t input /* @in */, + uint8_t output[] /* @out @length:1 @maxlength:1 */) const = 0; // @brief Fills an output buffer and returns the payload length directly. - // Intended to cover the @length:return annotation in a signature + // Intended to cover the length:return annotation in a signature // where return type carries the produced byte count. // @param output Receives payload bytes. // @param maxSize Maximum writable bytes in output. diff --git a/tests/FunctionalTests/comrpc/CMakeLists.txt b/tests/FunctionalTests/comrpc/CMakeLists.txt index 1e8aff83..2d0c7370 100644 --- a/tests/FunctionalTests/comrpc/CMakeLists.txt +++ b/tests/FunctionalTests/comrpc/CMakeLists.txt @@ -23,8 +23,8 @@ if(TEST_ASYNC) target_sources(ComRpcFunctionalTests PRIVATE tests/TestAsync.cpp) endif() -if(TEST_BUFFER) - target_sources(ComRpcFunctionalTests PRIVATE tests/TestBuffer.cpp) +if(TEST_BUFFERS) + target_sources(ComRpcFunctionalTests PRIVATE tests/TestBuffers.cpp) endif() if(TEST_ENUMS) @@ -43,8 +43,8 @@ if(TEST_ITERATORS) target_sources(ComRpcFunctionalTests PRIVATE tests/TestIterators.cpp) endif() -if(TEST_OPTIONAL) - target_sources(ComRpcFunctionalTests PRIVATE tests/TestOptional.cpp) +if(TEST_OPTIONALS) + target_sources(ComRpcFunctionalTests PRIVATE tests/TestOptionals.cpp) endif() if(TEST_PRIMITIVES) @@ -59,6 +59,22 @@ if(TEST_STRUCTS) target_sources(ComRpcFunctionalTests PRIVATE tests/TestStructs.cpp) endif() +if(TEST_ENCODING_MAC) + target_sources(ComRpcFunctionalTests PRIVATE tests/TestEncodingMac.cpp) +endif() + +if(TEST_LENGTH_MODES) + target_sources(ComRpcFunctionalTests PRIVATE tests/TestLengthModes.cpp) +endif() + +if(TEST_JSON_SHAPE) + target_sources(ComRpcFunctionalTests PRIVATE tests/TestJsonShape.cpp) +endif() + +if(TEST_JSON_FORMATTING) + target_sources(ComRpcFunctionalTests PRIVATE tests/TestJsonFormatting.cpp) +endif() + target_include_directories(ComRpcFunctionalTests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} # For interfaces/ prefix (generated code) diff --git a/tests/FunctionalTests/comrpc/tests/TestLengthModes.cpp b/tests/FunctionalTests/comrpc/tests/TestLengthModes.cpp index d6ccd082..efea1c88 100644 --- a/tests/FunctionalTests/comrpc/tests/TestLengthModes.cpp +++ b/tests/FunctionalTests/comrpc/tests/TestLengthModes.cpp @@ -27,11 +27,11 @@ using namespace Thunder::FunctionalTest; class TestLengthModes : public Testing::TestHarness {}; TEST_F(TestLengthModes, EchoSingleByte) { - const uint8_t input[1] = { 0x5A }; + const uint8_t input = 0x5A; uint8_t output[1] = { 0 }; ASSERT_EQ(_proxy->EchoSingleByte(input, output), Core::ERROR_NONE); - EXPECT_EQ(output[0], input[0]); + EXPECT_EQ(output[0], input); } TEST_F(TestLengthModes, ReadPayloadHonorsCapacity) { diff --git a/tests/FunctionalTests/jsonrpc/CMakeLists.txt b/tests/FunctionalTests/jsonrpc/CMakeLists.txt index 27ffefcf..bc881542 100644 --- a/tests/FunctionalTests/jsonrpc/CMakeLists.txt +++ b/tests/FunctionalTests/jsonrpc/CMakeLists.txt @@ -41,6 +41,20 @@ if(TEST_RESTRICTIONS) target_sources(JsonRpcFunctionalTests PRIVATE tests/TestRestrictionsJsonRpc.cpp) endif() +if(TEST_ENCODING_MAC) + target_sources(JsonRpcFunctionalTests PRIVATE tests/TestEncodingMacJsonRpc.cpp) +endif() + +if(TEST_JSON_SHAPE) + target_sources(JsonRpcFunctionalTests PRIVATE tests/TestJsonShapeJsonRpc.cpp) +endif() + +if(TEST_JSON_FORMATTING) + target_sources(JsonRpcFunctionalTests PRIVATE tests/TestJsonFormattingJsonRpc.cpp) +endif() + +# TEST_LENGTH_MODES is COM-RPC only — @length:return uses uint16_t return type which JsonGenerator does not support + target_link_libraries(JsonRpcFunctionalTests PRIVATE thunder_test_support diff --git a/tests/FunctionalTests/jsonrpc/tests/TestJsonFormattingJsonRpc.cpp b/tests/FunctionalTests/jsonrpc/tests/TestJsonFormattingJsonRpc.cpp index ab9f8069..df5a6d6a 100644 --- a/tests/FunctionalTests/jsonrpc/tests/TestJsonFormattingJsonRpc.cpp +++ b/tests/FunctionalTests/jsonrpc/tests/TestJsonFormattingJsonRpc.cpp @@ -31,30 +31,30 @@ TEST_F(TestJsonFormattingJsonRpc, PingCompliant) { EXPECT_FALSE(response.empty()); } -TEST_F(TestJsonFormattingJsonRpc, DISABLED_TextKeepNaming) { +TEST_F(TestJsonFormattingJsonRpc, TextKeepNaming) { string response; EXPECT_EQ(Core::ERROR_NONE, - CallMethod("echoMixedCaseName", R"({"InputValue":77})", response)); + CallMethod("keep", R"({"InputValue":77})", response)); EXPECT_FALSE(response.empty()); } -TEST_F(TestJsonFormattingJsonRpc, DISABLED_TextCaseNaming) { +TEST_F(TestJsonFormattingJsonRpc, TextCaseNaming) { string response; EXPECT_EQ(Core::ERROR_NONE, - CallMethod("echoCaseConvention", R"({"sourceValue":123})", response)); + CallMethod("echocaseconvention", R"({"sourcevalue":123})", response)); EXPECT_FALSE(response.empty()); } -TEST_F(TestJsonFormattingJsonRpc, DISABLED_UncompliantExtendedShape) { +TEST_F(TestJsonFormattingJsonRpc, UncompliantExtendedShape) { string response; EXPECT_EQ(Core::ERROR_NONE, CallMethod("pingExtended", R"({"payload":"abc"})", response)); EXPECT_FALSE(response.empty()); } -TEST_F(TestJsonFormattingJsonRpc, DISABLED_UncompliantCollapsedShape) { +TEST_F(TestJsonFormattingJsonRpc, UncompliantCollapsedShape) { string response; EXPECT_EQ(Core::ERROR_NONE, - CallMethod("pingCollapsed", R"({"payload":"abc"})", response)); + CallMethod("pingCollapsed", R"("abc")", response)); EXPECT_FALSE(response.empty()); } From 4598ea83e5c17deb1190486075dd406247afc340 Mon Sep 17 00:00:00 2001 From: smanes0213 Date: Tue, 16 Jun 2026 10:44:02 +0530 Subject: [PATCH 05/29] refactor: split ITestJsonFormatting into per-interface files, add respective implementation files --- tests/FunctionalTests/CMakeLists.txt | 12 +- tests/FunctionalTests/common/CMakeLists.txt | 50 +++--- .../implementations/TestJsonCompliantImpl.cpp | 48 ++++++ .../TestJsonFormattingImpl.cpp | 147 ------------------ .../implementations/TestJsonTextCaseImpl.cpp | 48 ++++++ .../implementations/TestJsonTextKeepImpl.cpp | 57 +++++++ .../TestJsonUncompliantCollapsedImpl.cpp | 48 ++++++ .../TestJsonUncompliantExtendedImpl.cpp | 48 ++++++ .../common/interfaces/ITestJsonCompliant.h | 41 +++++ .../common/interfaces/ITestJsonFormatting.h | 96 ------------ .../common/interfaces/ITestJsonTextCase.h | 41 +++++ .../common/interfaces/ITestJsonTextKeep.h | 48 ++++++ .../ITestJsonUncompliantCollapsed.h | 41 +++++ .../interfaces/ITestJsonUncompliantExtended.h | 41 +++++ tests/FunctionalTests/comrpc/CMakeLists.txt | 2 +- .../comrpc/tests/TestJsonFormatting.cpp | 6 +- tests/FunctionalTests/jsonrpc/CMakeLists.txt | 2 +- .../tests/TestJsonFormattingJsonRpc.cpp | 6 +- 18 files changed, 499 insertions(+), 283 deletions(-) create mode 100644 tests/FunctionalTests/common/implementations/TestJsonCompliantImpl.cpp delete mode 100644 tests/FunctionalTests/common/implementations/TestJsonFormattingImpl.cpp create mode 100644 tests/FunctionalTests/common/implementations/TestJsonTextCaseImpl.cpp create mode 100644 tests/FunctionalTests/common/implementations/TestJsonTextKeepImpl.cpp create mode 100644 tests/FunctionalTests/common/implementations/TestJsonUncompliantCollapsedImpl.cpp create mode 100644 tests/FunctionalTests/common/implementations/TestJsonUncompliantExtendedImpl.cpp create mode 100644 tests/FunctionalTests/common/interfaces/ITestJsonCompliant.h delete mode 100644 tests/FunctionalTests/common/interfaces/ITestJsonFormatting.h create mode 100644 tests/FunctionalTests/common/interfaces/ITestJsonTextCase.h create mode 100644 tests/FunctionalTests/common/interfaces/ITestJsonTextKeep.h create mode 100644 tests/FunctionalTests/common/interfaces/ITestJsonUncompliantCollapsed.h create mode 100644 tests/FunctionalTests/common/interfaces/ITestJsonUncompliantExtended.h diff --git a/tests/FunctionalTests/CMakeLists.txt b/tests/FunctionalTests/CMakeLists.txt index 61809531..d9bffc43 100644 --- a/tests/FunctionalTests/CMakeLists.txt +++ b/tests/FunctionalTests/CMakeLists.txt @@ -25,10 +25,14 @@ 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_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) -option(TEST_JSON_FORMATTING "Enable @text/@compliant/@uncompliant 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) +option(TEST_JSON_TEXT_KEEP "Enable @text:keep naming tests" ON) +option(TEST_JSON_TEXT_CASE "Enable @text:legacy case convention tests" ON) +option(TEST_JSON_COMPLIANT "Enable @compliant format tests" ON) +option(TEST_JSON_UNCOMPLIANT_EXT "Enable @uncompliant:extended format tests" ON) +option(TEST_JSON_UNCOMPLIANT_COL "Enable @uncompliant:collapsed format tests" ON) set(CMAKE_MODULE_PATH "${CMAKE_BINARY_DIR}" CACHE BOOL "" FORCE) diff --git a/tests/FunctionalTests/common/CMakeLists.txt b/tests/FunctionalTests/common/CMakeLists.txt index 8a12f0ce..ef4ca733 100644 --- a/tests/FunctionalTests/common/CMakeLists.txt +++ b/tests/FunctionalTests/common/CMakeLists.txt @@ -167,38 +167,24 @@ if (TEST_JSON_SHAPE) AddTestInterface("JsonShape" COM_RPC JSON_RPC) endif() -if (TEST_JSON_FORMATTING) - # Multiple @json interfaces in one header — register each sub-interface individually - # COM-RPC ProxyStubs are also needed for the COM-RPC test harness - target_sources(FunctionalTestCommon PRIVATE - "${CMAKE_CURRENT_SOURCE_DIR}/implementations/TestJsonFormattingImpl.cpp" - ) - list(APPEND COM_RPC_INTERFACE_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/interfaces/ITestJsonFormatting.h") - list(APPEND JSON_INTERFACE_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/interfaces/ITestJsonFormatting.h") - string(APPEND JSON_RPC_REGISTRATIONS - " FunctionalTest::JTestJsonTextKeep::Register(module,\n" - " static_cast(\n" - " TestImplementation::Factory::Instance().Create(FunctionalTest::ITestJsonTextKeep::ID)));\n\n" - " FunctionalTest::JTestJsonTextCase::Register(module,\n" - " static_cast(\n" - " TestImplementation::Factory::Instance().Create(FunctionalTest::ITestJsonTextCase::ID)));\n\n" - " FunctionalTest::JTestJsonCompliant::Register(module,\n" - " static_cast(\n" - " TestImplementation::Factory::Instance().Create(FunctionalTest::ITestJsonCompliant::ID)));\n\n" - " FunctionalTest::JTestJsonUncompliantExtended::Register(module,\n" - " static_cast(\n" - " TestImplementation::Factory::Instance().Create(FunctionalTest::ITestJsonUncompliantExtended::ID)));\n\n" - " FunctionalTest::JTestJsonUncompliantCollapsed::Register(module,\n" - " static_cast(\n" - " TestImplementation::Factory::Instance().Create(FunctionalTest::ITestJsonUncompliantCollapsed::ID)));\n\n" - ) - string(APPEND JSON_RPC_UNREGISTRATIONS - " FunctionalTest::JTestJsonTextKeep::Unregister(module);\n" - " FunctionalTest::JTestJsonTextCase::Unregister(module);\n" - " FunctionalTest::JTestJsonCompliant::Unregister(module);\n" - " FunctionalTest::JTestJsonUncompliantExtended::Unregister(module);\n" - " FunctionalTest::JTestJsonUncompliantCollapsed::Unregister(module);\n" - ) +if (TEST_JSON_TEXT_KEEP) + AddTestInterface("JsonTextKeep" COM_RPC JSON_RPC) +endif() + +if (TEST_JSON_TEXT_CASE) + AddTestInterface("JsonTextCase" COM_RPC JSON_RPC) +endif() + +if (TEST_JSON_COMPLIANT) + AddTestInterface("JsonCompliant" COM_RPC JSON_RPC) +endif() + +if (TEST_JSON_UNCOMPLIANT_EXT) + AddTestInterface("JsonUncompliantExtended" COM_RPC JSON_RPC) +endif() + +if (TEST_JSON_UNCOMPLIANT_COL) + AddTestInterface("JsonUncompliantCollapsed" COM_RPC JSON_RPC) endif() list(LENGTH COM_RPC_INTERFACE_HEADERS NUM_COM_TESTS) diff --git a/tests/FunctionalTests/common/implementations/TestJsonCompliantImpl.cpp b/tests/FunctionalTests/common/implementations/TestJsonCompliantImpl.cpp new file mode 100644 index 00000000..830e25e1 --- /dev/null +++ b/tests/FunctionalTests/common/implementations/TestJsonCompliantImpl.cpp @@ -0,0 +1,48 @@ +/* + * 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 + +namespace Thunder { +namespace TestImplementation { + + class TestJsonCompliantImpl : public FunctionalTest::ITestJsonCompliant { + public: + TestJsonCompliantImpl() = default; + ~TestJsonCompliantImpl() override = default; + + TestJsonCompliantImpl(const TestJsonCompliantImpl&) = delete; + TestJsonCompliantImpl& operator=(const TestJsonCompliantImpl&) = delete; + + Core::hresult Ping(const string& payload, string& reply) const override + { + reply = payload; + return Core::ERROR_NONE; + } + + BEGIN_INTERFACE_MAP(TestJsonCompliantImpl) + INTERFACE_ENTRY(FunctionalTest::ITestJsonCompliant) + END_INTERFACE_MAP + }; + + static Factory::Registrar g_registrar; + +} // namespace TestImplementation +} // namespace Thunder diff --git a/tests/FunctionalTests/common/implementations/TestJsonFormattingImpl.cpp b/tests/FunctionalTests/common/implementations/TestJsonFormattingImpl.cpp deleted file mode 100644 index 7185dc27..00000000 --- a/tests/FunctionalTests/common/implementations/TestJsonFormattingImpl.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/* - * 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 - -namespace Thunder { -namespace TestImplementation { - - // ===== ITestJsonTextKeep ===== - - class TestJsonTextKeepImpl : public FunctionalTest::ITestJsonTextKeep { - public: - TestJsonTextKeepImpl() : _buildVersion(1) {} - ~TestJsonTextKeepImpl() override = default; - - TestJsonTextKeepImpl(const TestJsonTextKeepImpl&) = delete; - TestJsonTextKeepImpl& operator=(const TestJsonTextKeepImpl&) = delete; - - Core::hresult EchoMixedCaseName(const uint32_t InputValue, uint32_t& OutputValue) const override - { - OutputValue = InputValue; - return Core::ERROR_NONE; - } - - Core::hresult BuildVersion(uint32_t& BuildVersion) const override - { - BuildVersion = _buildVersion; - return Core::ERROR_NONE; - } - - BEGIN_INTERFACE_MAP(TestJsonTextKeepImpl) - INTERFACE_ENTRY(FunctionalTest::ITestJsonTextKeep) - END_INTERFACE_MAP - - private: - uint32_t _buildVersion; - }; - - // ===== ITestJsonTextCase ===== - - class TestJsonTextCaseImpl : public FunctionalTest::ITestJsonTextCase { - public: - TestJsonTextCaseImpl() = default; - ~TestJsonTextCaseImpl() override = default; - - TestJsonTextCaseImpl(const TestJsonTextCaseImpl&) = delete; - TestJsonTextCaseImpl& operator=(const TestJsonTextCaseImpl&) = delete; - - Core::hresult EchoCaseConvention(const uint16_t sourceValue, uint16_t& resultValue) const override - { - resultValue = sourceValue; - return Core::ERROR_NONE; - } - - BEGIN_INTERFACE_MAP(TestJsonTextCaseImpl) - INTERFACE_ENTRY(FunctionalTest::ITestJsonTextCase) - END_INTERFACE_MAP - }; - - // ===== ITestJsonCompliant ===== - - class TestJsonCompliantImpl : public FunctionalTest::ITestJsonCompliant { - public: - TestJsonCompliantImpl() = default; - ~TestJsonCompliantImpl() override = default; - - TestJsonCompliantImpl(const TestJsonCompliantImpl&) = delete; - TestJsonCompliantImpl& operator=(const TestJsonCompliantImpl&) = delete; - - Core::hresult Ping(const string& payload, string& reply) const override - { - reply = payload; - return Core::ERROR_NONE; - } - - BEGIN_INTERFACE_MAP(TestJsonCompliantImpl) - INTERFACE_ENTRY(FunctionalTest::ITestJsonCompliant) - END_INTERFACE_MAP - }; - - // ===== ITestJsonUncompliantExtended ===== - - class TestJsonUncompliantExtendedImpl : public FunctionalTest::ITestJsonUncompliantExtended { - public: - TestJsonUncompliantExtendedImpl() = default; - ~TestJsonUncompliantExtendedImpl() override = default; - - TestJsonUncompliantExtendedImpl(const TestJsonUncompliantExtendedImpl&) = delete; - TestJsonUncompliantExtendedImpl& operator=(const TestJsonUncompliantExtendedImpl&) = delete; - - Core::hresult PingExtended(const string& payload, string& reply) const override - { - reply = payload; - return Core::ERROR_NONE; - } - - BEGIN_INTERFACE_MAP(TestJsonUncompliantExtendedImpl) - INTERFACE_ENTRY(FunctionalTest::ITestJsonUncompliantExtended) - END_INTERFACE_MAP - }; - - // ===== ITestJsonUncompliantCollapsed ===== - - class TestJsonUncompliantCollapsedImpl : public FunctionalTest::ITestJsonUncompliantCollapsed { - public: - TestJsonUncompliantCollapsedImpl() = default; - ~TestJsonUncompliantCollapsedImpl() override = default; - - TestJsonUncompliantCollapsedImpl(const TestJsonUncompliantCollapsedImpl&) = delete; - TestJsonUncompliantCollapsedImpl& operator=(const TestJsonUncompliantCollapsedImpl&) = delete; - - Core::hresult PingCollapsed(const string& payload, string& reply) const override - { - reply = payload; - return Core::ERROR_NONE; - } - - BEGIN_INTERFACE_MAP(TestJsonUncompliantCollapsedImpl) - INTERFACE_ENTRY(FunctionalTest::ITestJsonUncompliantCollapsed) - END_INTERFACE_MAP - }; - - static Factory::Registrar g_jsonTextKeepRegistrar; - static Factory::Registrar g_jsonTextCaseRegistrar; - static Factory::Registrar g_jsonCompliantRegistrar; - static Factory::Registrar g_jsonUncompliantExtRegistrar; - static Factory::Registrar g_jsonUncompliantColRegistrar; - -} // namespace TestImplementation -} // namespace Thunder diff --git a/tests/FunctionalTests/common/implementations/TestJsonTextCaseImpl.cpp b/tests/FunctionalTests/common/implementations/TestJsonTextCaseImpl.cpp new file mode 100644 index 00000000..b5c139c2 --- /dev/null +++ b/tests/FunctionalTests/common/implementations/TestJsonTextCaseImpl.cpp @@ -0,0 +1,48 @@ +/* + * 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 + +namespace Thunder { +namespace TestImplementation { + + class TestJsonTextCaseImpl : public FunctionalTest::ITestJsonTextCase { + public: + TestJsonTextCaseImpl() = default; + ~TestJsonTextCaseImpl() override = default; + + TestJsonTextCaseImpl(const TestJsonTextCaseImpl&) = delete; + TestJsonTextCaseImpl& operator=(const TestJsonTextCaseImpl&) = delete; + + Core::hresult EchoCaseConvention(const uint16_t sourceValue, uint16_t& resultValue) const override + { + resultValue = sourceValue; + return Core::ERROR_NONE; + } + + BEGIN_INTERFACE_MAP(TestJsonTextCaseImpl) + INTERFACE_ENTRY(FunctionalTest::ITestJsonTextCase) + END_INTERFACE_MAP + }; + + static Factory::Registrar g_registrar; + +} // namespace TestImplementation +} // namespace Thunder diff --git a/tests/FunctionalTests/common/implementations/TestJsonTextKeepImpl.cpp b/tests/FunctionalTests/common/implementations/TestJsonTextKeepImpl.cpp new file mode 100644 index 00000000..1681409d --- /dev/null +++ b/tests/FunctionalTests/common/implementations/TestJsonTextKeepImpl.cpp @@ -0,0 +1,57 @@ +/* + * 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 + +namespace Thunder { +namespace TestImplementation { + + class TestJsonTextKeepImpl : public FunctionalTest::ITestJsonTextKeep { + public: + TestJsonTextKeepImpl() : _buildVersion(1) {} + ~TestJsonTextKeepImpl() override = default; + + TestJsonTextKeepImpl(const TestJsonTextKeepImpl&) = delete; + TestJsonTextKeepImpl& operator=(const TestJsonTextKeepImpl&) = delete; + + Core::hresult EchoMixedCaseName(const uint32_t InputValue, uint32_t& OutputValue) const override + { + OutputValue = InputValue; + return Core::ERROR_NONE; + } + + Core::hresult BuildVersion(uint32_t& BuildVersion) const override + { + BuildVersion = _buildVersion; + return Core::ERROR_NONE; + } + + BEGIN_INTERFACE_MAP(TestJsonTextKeepImpl) + INTERFACE_ENTRY(FunctionalTest::ITestJsonTextKeep) + END_INTERFACE_MAP + + private: + uint32_t _buildVersion; + }; + + static Factory::Registrar g_registrar; + +} // namespace TestImplementation +} // namespace Thunder diff --git a/tests/FunctionalTests/common/implementations/TestJsonUncompliantCollapsedImpl.cpp b/tests/FunctionalTests/common/implementations/TestJsonUncompliantCollapsedImpl.cpp new file mode 100644 index 00000000..52a06605 --- /dev/null +++ b/tests/FunctionalTests/common/implementations/TestJsonUncompliantCollapsedImpl.cpp @@ -0,0 +1,48 @@ +/* + * 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 + +namespace Thunder { +namespace TestImplementation { + + class TestJsonUncompliantCollapsedImpl : public FunctionalTest::ITestJsonUncompliantCollapsed { + public: + TestJsonUncompliantCollapsedImpl() = default; + ~TestJsonUncompliantCollapsedImpl() override = default; + + TestJsonUncompliantCollapsedImpl(const TestJsonUncompliantCollapsedImpl&) = delete; + TestJsonUncompliantCollapsedImpl& operator=(const TestJsonUncompliantCollapsedImpl&) = delete; + + Core::hresult PingCollapsed(const string& payload, string& reply) const override + { + reply = payload; + return Core::ERROR_NONE; + } + + BEGIN_INTERFACE_MAP(TestJsonUncompliantCollapsedImpl) + INTERFACE_ENTRY(FunctionalTest::ITestJsonUncompliantCollapsed) + END_INTERFACE_MAP + }; + + static Factory::Registrar g_registrar; + +} // namespace TestImplementation +} // namespace Thunder diff --git a/tests/FunctionalTests/common/implementations/TestJsonUncompliantExtendedImpl.cpp b/tests/FunctionalTests/common/implementations/TestJsonUncompliantExtendedImpl.cpp new file mode 100644 index 00000000..8a68bd66 --- /dev/null +++ b/tests/FunctionalTests/common/implementations/TestJsonUncompliantExtendedImpl.cpp @@ -0,0 +1,48 @@ +/* + * 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 + +namespace Thunder { +namespace TestImplementation { + + class TestJsonUncompliantExtendedImpl : public FunctionalTest::ITestJsonUncompliantExtended { + public: + TestJsonUncompliantExtendedImpl() = default; + ~TestJsonUncompliantExtendedImpl() override = default; + + TestJsonUncompliantExtendedImpl(const TestJsonUncompliantExtendedImpl&) = delete; + TestJsonUncompliantExtendedImpl& operator=(const TestJsonUncompliantExtendedImpl&) = delete; + + Core::hresult PingExtended(const string& payload, string& reply) const override + { + reply = payload; + return Core::ERROR_NONE; + } + + BEGIN_INTERFACE_MAP(TestJsonUncompliantExtendedImpl) + INTERFACE_ENTRY(FunctionalTest::ITestJsonUncompliantExtended) + END_INTERFACE_MAP + }; + + static Factory::Registrar g_registrar; + +} // namespace TestImplementation +} // namespace Thunder diff --git a/tests/FunctionalTests/common/interfaces/ITestJsonCompliant.h b/tests/FunctionalTests/common/interfaces/ITestJsonCompliant.h new file mode 100644 index 00000000..a6f94841 --- /dev/null +++ b/tests/FunctionalTests/common/interfaces/ITestJsonCompliant.h @@ -0,0 +1,41 @@ +/* + * 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 { + + // @json 1.0.0 + // @compliant + struct EXTERNAL ITestJsonCompliant : virtual public Core::IUnknown { + enum { ID = ID_TEST_JSON_COMPLIANT }; + + // @brief Baseline interface for compliant JSON-RPC format generation. + // @param payload Input payload. + // @param reply Receives output payload. + // @retval ERROR_NONE Operation completed. + virtual Core::hresult Ping(const string& payload /* @in */, string& reply /* @out */) const = 0; + }; + +} // namespace FunctionalTest +} // namespace Thunder diff --git a/tests/FunctionalTests/common/interfaces/ITestJsonFormatting.h b/tests/FunctionalTests/common/interfaces/ITestJsonFormatting.h deleted file mode 100644 index 8df105a3..00000000 --- a/tests/FunctionalTests/common/interfaces/ITestJsonFormatting.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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 { - - // @json 1.0.0 - // @text:keep - struct EXTERNAL ITestJsonTextKeep : virtual public Core::IUnknown { - enum { ID = ID_TEST_JSON_TEXT_KEEP }; - - // @brief Verifies that the original C++ identifier casing is preserved - // in generated JSON names when @text:keep is enabled. - // @param InputValue Input value used for round-trip verification. - // @param OutputValue Receives echoed value. - // @retval ERROR_NONE Echo completed. - virtual Core::hresult EchoMixedCaseName(const uint32_t InputValue /* @in */, uint32_t& OutputValue /* @out */) const = 0; - - // @property - // @brief Build version property. - // @param BuildVersion Receives current build version value. - // @retval ERROR_NONE Value returned. - virtual Core::hresult BuildVersion(uint32_t& BuildVersion /* @out */) const = 0; - }; - - // @json 1.0.0 - // @text:legacy - struct EXTERNAL ITestJsonTextCase : virtual public Core::IUnknown { - enum { ID = ID_TEST_JSON_TEXT_CASE }; - - // @brief Verifies case-convention transformation in generated JSON names. - // @param sourceValue Input value for round-trip verification. - // @param resultValue Receives echoed value. - // @retval ERROR_NONE Echo completed. - virtual Core::hresult EchoCaseConvention(const uint16_t sourceValue /* @in */, uint16_t& resultValue /* @out */) const = 0; - }; - - // @json 1.0.0 - // @compliant - struct EXTERNAL ITestJsonCompliant : virtual public Core::IUnknown { - enum { ID = ID_TEST_JSON_COMPLIANT }; - - // @brief Baseline interface for compliant JSON-RPC format generation. - // @param payload Input payload. - // @param reply Receives output payload. - // @retval ERROR_NONE Operation completed. - virtual Core::hresult Ping(const string& payload /* @in */, string& reply /* @out */) const = 0; - }; - - // @json 1.0.0 - // @uncompliant:extended - struct EXTERNAL ITestJsonUncompliantExtended : virtual public Core::IUnknown { - enum { ID = ID_TEST_JSON_UNCOMPLIANT_EXT }; - - // @brief Interface-level tag target for extended uncompliant mode. - // @param payload Input payload. - // @param reply Receives output payload. - // @retval ERROR_NONE Operation completed. - virtual Core::hresult PingExtended(const string& payload /* @in */, string& reply /* @out */) const = 0; - }; - - // @json 1.0.0 - // @uncompliant:collapsed - struct EXTERNAL ITestJsonUncompliantCollapsed : virtual public Core::IUnknown { - enum { ID = ID_TEST_JSON_UNCOMPLIANT_COL }; - - // @brief Interface-level tag target for collapsed uncompliant mode. - // @param payload Input payload. - // @param reply Receives output payload. - // @retval ERROR_NONE Operation completed. - virtual Core::hresult PingCollapsed(const string& payload /* @in */, string& reply /* @out */) const = 0; - }; - -} // namespace FunctionalTest -} // namespace Thunder diff --git a/tests/FunctionalTests/common/interfaces/ITestJsonTextCase.h b/tests/FunctionalTests/common/interfaces/ITestJsonTextCase.h new file mode 100644 index 00000000..495be180 --- /dev/null +++ b/tests/FunctionalTests/common/interfaces/ITestJsonTextCase.h @@ -0,0 +1,41 @@ +/* + * 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 { + + // @json 1.0.0 + // @text:legacy + struct EXTERNAL ITestJsonTextCase : virtual public Core::IUnknown { + enum { ID = ID_TEST_JSON_TEXT_CASE }; + + // @brief Verifies case-convention transformation in generated JSON names. + // @param sourceValue Input value for round-trip verification. + // @param resultValue Receives echoed value. + // @retval ERROR_NONE Echo completed. + virtual Core::hresult EchoCaseConvention(const uint16_t sourceValue /* @in */, uint16_t& resultValue /* @out */) const = 0; + }; + +} // namespace FunctionalTest +} // namespace Thunder diff --git a/tests/FunctionalTests/common/interfaces/ITestJsonTextKeep.h b/tests/FunctionalTests/common/interfaces/ITestJsonTextKeep.h new file mode 100644 index 00000000..fd2701e4 --- /dev/null +++ b/tests/FunctionalTests/common/interfaces/ITestJsonTextKeep.h @@ -0,0 +1,48 @@ +/* + * 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 { + + // @json 1.0.0 + // @text:keep + struct EXTERNAL ITestJsonTextKeep : virtual public Core::IUnknown { + enum { ID = ID_TEST_JSON_TEXT_KEEP }; + + // @brief Verifies that the original C++ identifier casing is preserved + // in generated JSON names when @text:keep is enabled. + // @param InputValue Input value used for round-trip verification. + // @param OutputValue Receives echoed value. + // @retval ERROR_NONE Echo completed. + virtual Core::hresult EchoMixedCaseName(const uint32_t InputValue /* @in */, uint32_t& OutputValue /* @out */) const = 0; + + // @property + // @brief Build version property. + // @param BuildVersion Receives current build version value. + // @retval ERROR_NONE Value returned. + virtual Core::hresult BuildVersion(uint32_t& BuildVersion /* @out */) const = 0; + }; + +} // namespace FunctionalTest +} // namespace Thunder diff --git a/tests/FunctionalTests/common/interfaces/ITestJsonUncompliantCollapsed.h b/tests/FunctionalTests/common/interfaces/ITestJsonUncompliantCollapsed.h new file mode 100644 index 00000000..b30e7c66 --- /dev/null +++ b/tests/FunctionalTests/common/interfaces/ITestJsonUncompliantCollapsed.h @@ -0,0 +1,41 @@ +/* + * 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 { + + // @json 1.0.0 + // @uncompliant:collapsed + struct EXTERNAL ITestJsonUncompliantCollapsed : virtual public Core::IUnknown { + enum { ID = ID_TEST_JSON_UNCOMPLIANT_COL }; + + // @brief Interface-level tag target for collapsed uncompliant mode. + // @param payload Input payload. + // @param reply Receives output payload. + // @retval ERROR_NONE Operation completed. + virtual Core::hresult PingCollapsed(const string& payload /* @in */, string& reply /* @out */) const = 0; + }; + +} // namespace FunctionalTest +} // namespace Thunder diff --git a/tests/FunctionalTests/common/interfaces/ITestJsonUncompliantExtended.h b/tests/FunctionalTests/common/interfaces/ITestJsonUncompliantExtended.h new file mode 100644 index 00000000..79fefd60 --- /dev/null +++ b/tests/FunctionalTests/common/interfaces/ITestJsonUncompliantExtended.h @@ -0,0 +1,41 @@ +/* + * 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 { + + // @json 1.0.0 + // @uncompliant:extended + struct EXTERNAL ITestJsonUncompliantExtended : virtual public Core::IUnknown { + enum { ID = ID_TEST_JSON_UNCOMPLIANT_EXT }; + + // @brief Interface-level tag target for extended uncompliant mode. + // @param payload Input payload. + // @param reply Receives output payload. + // @retval ERROR_NONE Operation completed. + virtual Core::hresult PingExtended(const string& payload /* @in */, string& reply /* @out */) const = 0; + }; + +} // namespace FunctionalTest +} // namespace Thunder diff --git a/tests/FunctionalTests/comrpc/CMakeLists.txt b/tests/FunctionalTests/comrpc/CMakeLists.txt index 2d0c7370..ac64ddbc 100644 --- a/tests/FunctionalTests/comrpc/CMakeLists.txt +++ b/tests/FunctionalTests/comrpc/CMakeLists.txt @@ -71,7 +71,7 @@ if(TEST_JSON_SHAPE) target_sources(ComRpcFunctionalTests PRIVATE tests/TestJsonShape.cpp) endif() -if(TEST_JSON_FORMATTING) +if(TEST_JSON_TEXT_KEEP OR TEST_JSON_TEXT_CASE OR TEST_JSON_COMPLIANT OR TEST_JSON_UNCOMPLIANT_EXT OR TEST_JSON_UNCOMPLIANT_COL) target_sources(ComRpcFunctionalTests PRIVATE tests/TestJsonFormatting.cpp) endif() diff --git a/tests/FunctionalTests/comrpc/tests/TestJsonFormatting.cpp b/tests/FunctionalTests/comrpc/tests/TestJsonFormatting.cpp index cac0902a..6706a28e 100644 --- a/tests/FunctionalTests/comrpc/tests/TestJsonFormatting.cpp +++ b/tests/FunctionalTests/comrpc/tests/TestJsonFormatting.cpp @@ -19,7 +19,11 @@ #include #include "TestHarness.h" -#include +#include +#include +#include +#include +#include using namespace Thunder; using namespace Thunder::FunctionalTest; diff --git a/tests/FunctionalTests/jsonrpc/CMakeLists.txt b/tests/FunctionalTests/jsonrpc/CMakeLists.txt index bc881542..43486f4e 100644 --- a/tests/FunctionalTests/jsonrpc/CMakeLists.txt +++ b/tests/FunctionalTests/jsonrpc/CMakeLists.txt @@ -49,7 +49,7 @@ if(TEST_JSON_SHAPE) target_sources(JsonRpcFunctionalTests PRIVATE tests/TestJsonShapeJsonRpc.cpp) endif() -if(TEST_JSON_FORMATTING) +if(TEST_JSON_TEXT_KEEP OR TEST_JSON_TEXT_CASE OR TEST_JSON_COMPLIANT OR TEST_JSON_UNCOMPLIANT_EXT OR TEST_JSON_UNCOMPLIANT_COL) target_sources(JsonRpcFunctionalTests PRIVATE tests/TestJsonFormattingJsonRpc.cpp) endif() diff --git a/tests/FunctionalTests/jsonrpc/tests/TestJsonFormattingJsonRpc.cpp b/tests/FunctionalTests/jsonrpc/tests/TestJsonFormattingJsonRpc.cpp index df5a6d6a..e39891d9 100644 --- a/tests/FunctionalTests/jsonrpc/tests/TestJsonFormattingJsonRpc.cpp +++ b/tests/FunctionalTests/jsonrpc/tests/TestJsonFormattingJsonRpc.cpp @@ -19,7 +19,11 @@ #include #include "JsonRpcTestHarness.h" -#include +#include +#include +#include +#include +#include using namespace Thunder; From 695afd6308808ca4b73082b9ecae28804d61ff16 Mon Sep 17 00:00:00 2001 From: smanes0213 Date: Tue, 16 Jun 2026 12:18:56 +0530 Subject: [PATCH 06/29] Resolve review comments --- .../common/interfaces/ITestJsonShape.h | 4 +- .../common/interfaces/ITestJsonTextKeep.h | 2 +- tests/FunctionalTests/comrpc/CMakeLists.txt | 20 +++++- .../comrpc/tests/TestJsonCompliant.cpp | 32 ++++++++++ .../comrpc/tests/TestJsonTextCase.cpp | 33 ++++++++++ ...sonFormatting.cpp => TestJsonTextKeep.cpp} | 29 --------- .../tests/TestJsonUncompliantCollapsed.cpp | 32 ++++++++++ .../tests/TestJsonUncompliantExtended.cpp | 32 ++++++++++ tests/FunctionalTests/jsonrpc/CMakeLists.txt | 20 +++++- .../FunctionalTests/jsonrpc/JsonRpcServer.cpp | 2 + .../jsonrpc/tests/TestEncodingMacJsonRpc.cpp | 6 +- .../tests/TestJsonCompliantJsonRpc.cpp | 33 ++++++++++ .../tests/TestJsonFormattingJsonRpc.cpp | 64 ------------------- .../jsonrpc/tests/TestJsonShapeJsonRpc.cpp | 10 ++- ...sonRpc.cpp => TestJsonTextCaseJsonRpc.cpp} | 18 ++---- .../jsonrpc/tests/TestJsonTextKeepJsonRpc.cpp | 42 ++++++++++++ .../TestJsonUncompliantCollapsedJsonRpc.cpp | 34 ++++++++++ .../TestJsonUncompliantExtendedJsonRpc.cpp | 34 ++++++++++ 18 files changed, 330 insertions(+), 117 deletions(-) create mode 100644 tests/FunctionalTests/comrpc/tests/TestJsonCompliant.cpp create mode 100644 tests/FunctionalTests/comrpc/tests/TestJsonTextCase.cpp rename tests/FunctionalTests/comrpc/tests/{TestJsonFormatting.cpp => TestJsonTextKeep.cpp} (53%) create mode 100644 tests/FunctionalTests/comrpc/tests/TestJsonUncompliantCollapsed.cpp create mode 100644 tests/FunctionalTests/comrpc/tests/TestJsonUncompliantExtended.cpp create mode 100644 tests/FunctionalTests/jsonrpc/tests/TestJsonCompliantJsonRpc.cpp delete mode 100644 tests/FunctionalTests/jsonrpc/tests/TestJsonFormattingJsonRpc.cpp rename tests/FunctionalTests/jsonrpc/tests/{TestLengthModesJsonRpc.cpp => TestJsonTextCaseJsonRpc.cpp} (64%) create mode 100644 tests/FunctionalTests/jsonrpc/tests/TestJsonTextKeepJsonRpc.cpp create mode 100644 tests/FunctionalTests/jsonrpc/tests/TestJsonUncompliantCollapsedJsonRpc.cpp create mode 100644 tests/FunctionalTests/jsonrpc/tests/TestJsonUncompliantExtendedJsonRpc.cpp diff --git a/tests/FunctionalTests/common/interfaces/ITestJsonShape.h b/tests/FunctionalTests/common/interfaces/ITestJsonShape.h index defb64a8..3ed3acf2 100644 --- a/tests/FunctionalTests/common/interfaces/ITestJsonShape.h +++ b/tests/FunctionalTests/common/interfaces/ITestJsonShape.h @@ -48,13 +48,13 @@ namespace FunctionalTest { const std::vector& input /* @in @extract @restrict:1..64 */, std::vector& output /* @out @extract @restrict:1..64 */) const = 0; - // @brief Echoes a struct and requests collapsed representation in JSON. + // @brief Echoes a struct using the standard (non-extracted) JSON representation. struct Dimensions { uint16_t width; uint16_t height; }; - // @brief Echoes a struct and requests collapsed representation in JSON. + // @brief Echoes a struct using the standard (non-extracted) JSON representation. // @param in Input Dimensions value. // @param out Receives echoed Dimensions value. // @retval ERROR_NONE Echo completed. diff --git a/tests/FunctionalTests/common/interfaces/ITestJsonTextKeep.h b/tests/FunctionalTests/common/interfaces/ITestJsonTextKeep.h index fd2701e4..f2d47aba 100644 --- a/tests/FunctionalTests/common/interfaces/ITestJsonTextKeep.h +++ b/tests/FunctionalTests/common/interfaces/ITestJsonTextKeep.h @@ -31,7 +31,7 @@ namespace FunctionalTest { enum { ID = ID_TEST_JSON_TEXT_KEEP }; // @brief Verifies that the original C++ identifier casing is preserved - // in generated JSON names when @text:keep is enabled. + // in generated JSON names when the keep case convention is active. // @param InputValue Input value used for round-trip verification. // @param OutputValue Receives echoed value. // @retval ERROR_NONE Echo completed. diff --git a/tests/FunctionalTests/comrpc/CMakeLists.txt b/tests/FunctionalTests/comrpc/CMakeLists.txt index ac64ddbc..3b0d7826 100644 --- a/tests/FunctionalTests/comrpc/CMakeLists.txt +++ b/tests/FunctionalTests/comrpc/CMakeLists.txt @@ -71,8 +71,24 @@ if(TEST_JSON_SHAPE) target_sources(ComRpcFunctionalTests PRIVATE tests/TestJsonShape.cpp) endif() -if(TEST_JSON_TEXT_KEEP OR TEST_JSON_TEXT_CASE OR TEST_JSON_COMPLIANT OR TEST_JSON_UNCOMPLIANT_EXT OR TEST_JSON_UNCOMPLIANT_COL) - target_sources(ComRpcFunctionalTests PRIVATE tests/TestJsonFormatting.cpp) +if(TEST_JSON_TEXT_KEEP) + target_sources(ComRpcFunctionalTests PRIVATE tests/TestJsonTextKeep.cpp) +endif() + +if(TEST_JSON_TEXT_CASE) + target_sources(ComRpcFunctionalTests PRIVATE tests/TestJsonTextCase.cpp) +endif() + +if(TEST_JSON_COMPLIANT) + target_sources(ComRpcFunctionalTests PRIVATE tests/TestJsonCompliant.cpp) +endif() + +if(TEST_JSON_UNCOMPLIANT_EXT) + target_sources(ComRpcFunctionalTests PRIVATE tests/TestJsonUncompliantExtended.cpp) +endif() + +if(TEST_JSON_UNCOMPLIANT_COL) + target_sources(ComRpcFunctionalTests PRIVATE tests/TestJsonUncompliantCollapsed.cpp) endif() target_include_directories(ComRpcFunctionalTests diff --git a/tests/FunctionalTests/comrpc/tests/TestJsonCompliant.cpp b/tests/FunctionalTests/comrpc/tests/TestJsonCompliant.cpp new file mode 100644 index 00000000..b34542ee --- /dev/null +++ b/tests/FunctionalTests/comrpc/tests/TestJsonCompliant.cpp @@ -0,0 +1,32 @@ +/* + * 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 TestJsonCompliant : public Testing::TestHarness {}; + +TEST_F(TestJsonCompliant, Ping) { + string reply; + ASSERT_EQ(_proxy->Ping("payload", reply), Core::ERROR_NONE); +} diff --git a/tests/FunctionalTests/comrpc/tests/TestJsonTextCase.cpp b/tests/FunctionalTests/comrpc/tests/TestJsonTextCase.cpp new file mode 100644 index 00000000..6458afa9 --- /dev/null +++ b/tests/FunctionalTests/comrpc/tests/TestJsonTextCase.cpp @@ -0,0 +1,33 @@ +/* + * 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 TestJsonTextCase : public Testing::TestHarness {}; + +TEST_F(TestJsonTextCase, EchoCaseConvention) { + uint16_t result = 0; + ASSERT_EQ(_proxy->EchoCaseConvention(123, result), Core::ERROR_NONE); + EXPECT_EQ(result, 123u); +} diff --git a/tests/FunctionalTests/comrpc/tests/TestJsonFormatting.cpp b/tests/FunctionalTests/comrpc/tests/TestJsonTextKeep.cpp similarity index 53% rename from tests/FunctionalTests/comrpc/tests/TestJsonFormatting.cpp rename to tests/FunctionalTests/comrpc/tests/TestJsonTextKeep.cpp index 6706a28e..7ff10df9 100644 --- a/tests/FunctionalTests/comrpc/tests/TestJsonFormatting.cpp +++ b/tests/FunctionalTests/comrpc/tests/TestJsonTextKeep.cpp @@ -20,19 +20,11 @@ #include #include "TestHarness.h" #include -#include -#include -#include -#include using namespace Thunder; using namespace Thunder::FunctionalTest; class TestJsonTextKeep : public Testing::TestHarness {}; -class TestJsonTextCase : public Testing::TestHarness {}; -class TestJsonCompliant : public Testing::TestHarness {}; -class TestJsonUncompliantExtended : public Testing::TestHarness {}; -class TestJsonUncompliantCollapsed : public Testing::TestHarness {}; TEST_F(TestJsonTextKeep, EchoMixedCaseName) { uint32_t output = 0; @@ -44,24 +36,3 @@ TEST_F(TestJsonTextKeep, BuildVersionProperty) { uint32_t version = 0; ASSERT_EQ(_proxy->BuildVersion(version), Core::ERROR_NONE); } - -TEST_F(TestJsonTextCase, EchoCaseConvention) { - uint16_t result = 0; - ASSERT_EQ(_proxy->EchoCaseConvention(123, result), Core::ERROR_NONE); - EXPECT_EQ(result, 123u); -} - -TEST_F(TestJsonCompliant, Ping) { - string reply; - ASSERT_EQ(_proxy->Ping("payload", reply), Core::ERROR_NONE); -} - -TEST_F(TestJsonUncompliantExtended, PingExtended) { - string reply; - ASSERT_EQ(_proxy->PingExtended("payload", reply), Core::ERROR_NONE); -} - -TEST_F(TestJsonUncompliantCollapsed, PingCollapsed) { - string reply; - ASSERT_EQ(_proxy->PingCollapsed("payload", reply), Core::ERROR_NONE); -} diff --git a/tests/FunctionalTests/comrpc/tests/TestJsonUncompliantCollapsed.cpp b/tests/FunctionalTests/comrpc/tests/TestJsonUncompliantCollapsed.cpp new file mode 100644 index 00000000..c7cb5ef2 --- /dev/null +++ b/tests/FunctionalTests/comrpc/tests/TestJsonUncompliantCollapsed.cpp @@ -0,0 +1,32 @@ +/* + * 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 TestJsonUncompliantCollapsed : public Testing::TestHarness {}; + +TEST_F(TestJsonUncompliantCollapsed, PingCollapsed) { + string reply; + ASSERT_EQ(_proxy->PingCollapsed("payload", reply), Core::ERROR_NONE); +} diff --git a/tests/FunctionalTests/comrpc/tests/TestJsonUncompliantExtended.cpp b/tests/FunctionalTests/comrpc/tests/TestJsonUncompliantExtended.cpp new file mode 100644 index 00000000..d96f848e --- /dev/null +++ b/tests/FunctionalTests/comrpc/tests/TestJsonUncompliantExtended.cpp @@ -0,0 +1,32 @@ +/* + * 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 TestJsonUncompliantExtended : public Testing::TestHarness {}; + +TEST_F(TestJsonUncompliantExtended, PingExtended) { + string reply; + ASSERT_EQ(_proxy->PingExtended("payload", reply), Core::ERROR_NONE); +} diff --git a/tests/FunctionalTests/jsonrpc/CMakeLists.txt b/tests/FunctionalTests/jsonrpc/CMakeLists.txt index 43486f4e..f09e42a5 100644 --- a/tests/FunctionalTests/jsonrpc/CMakeLists.txt +++ b/tests/FunctionalTests/jsonrpc/CMakeLists.txt @@ -49,8 +49,24 @@ if(TEST_JSON_SHAPE) target_sources(JsonRpcFunctionalTests PRIVATE tests/TestJsonShapeJsonRpc.cpp) endif() -if(TEST_JSON_TEXT_KEEP OR TEST_JSON_TEXT_CASE OR TEST_JSON_COMPLIANT OR TEST_JSON_UNCOMPLIANT_EXT OR TEST_JSON_UNCOMPLIANT_COL) - target_sources(JsonRpcFunctionalTests PRIVATE tests/TestJsonFormattingJsonRpc.cpp) +if(TEST_JSON_TEXT_KEEP) + target_sources(JsonRpcFunctionalTests PRIVATE tests/TestJsonTextKeepJsonRpc.cpp) +endif() + +if(TEST_JSON_TEXT_CASE) + target_sources(JsonRpcFunctionalTests PRIVATE tests/TestJsonTextCaseJsonRpc.cpp) +endif() + +if(TEST_JSON_COMPLIANT) + target_sources(JsonRpcFunctionalTests PRIVATE tests/TestJsonCompliantJsonRpc.cpp) +endif() + +if(TEST_JSON_UNCOMPLIANT_EXT) + target_sources(JsonRpcFunctionalTests PRIVATE tests/TestJsonUncompliantExtendedJsonRpc.cpp) +endif() + +if(TEST_JSON_UNCOMPLIANT_COL) + target_sources(JsonRpcFunctionalTests PRIVATE tests/TestJsonUncompliantCollapsedJsonRpc.cpp) endif() # TEST_LENGTH_MODES is COM-RPC only — @length:return uses uint16_t return type which JsonGenerator does not support diff --git a/tests/FunctionalTests/jsonrpc/JsonRpcServer.cpp b/tests/FunctionalTests/jsonrpc/JsonRpcServer.cpp index e2636285..4227e764 100644 --- a/tests/FunctionalTests/jsonrpc/JsonRpcServer.cpp +++ b/tests/FunctionalTests/jsonrpc/JsonRpcServer.cpp @@ -19,6 +19,8 @@ #include "JsonRpcServer.h" +#include + namespace Thunder { namespace JsonRpcServer { JsonRpcServer* g_server = nullptr; diff --git a/tests/FunctionalTests/jsonrpc/tests/TestEncodingMacJsonRpc.cpp b/tests/FunctionalTests/jsonrpc/tests/TestEncodingMacJsonRpc.cpp index 3259ebeb..dce35f95 100644 --- a/tests/FunctionalTests/jsonrpc/tests/TestEncodingMacJsonRpc.cpp +++ b/tests/FunctionalTests/jsonrpc/tests/TestEncodingMacJsonRpc.cpp @@ -33,12 +33,14 @@ TEST_F(TestEncodingMacJsonRpc, SetGetMacAddress) { response.clear(); EXPECT_EQ(Core::ERROR_NONE, CallMethod("getMacAddress", "{}", response)); - EXPECT_FALSE(response.empty()); + // verify the stored MAC is returned correctly + EXPECT_NE(response.find("01:23:45:67:89:ab"), string::npos); } TEST_F(TestEncodingMacJsonRpc, EchoMacAddress) { string response; EXPECT_EQ(Core::ERROR_NONE, CallMethod("echoMacAddress", R"({"input":"de:ad:be:ef:00:01"})", response)); - EXPECT_FALSE(response.empty()); + // verify the echoed MAC matches the input + EXPECT_NE(response.find("de:ad:be:ef:00:01"), string::npos); } diff --git a/tests/FunctionalTests/jsonrpc/tests/TestJsonCompliantJsonRpc.cpp b/tests/FunctionalTests/jsonrpc/tests/TestJsonCompliantJsonRpc.cpp new file mode 100644 index 00000000..37b4d164 --- /dev/null +++ b/tests/FunctionalTests/jsonrpc/tests/TestJsonCompliantJsonRpc.cpp @@ -0,0 +1,33 @@ +/* + * 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 "JsonRpcTestHarness.h" +#include + +using namespace Thunder; + +class TestJsonCompliantJsonRpc : public JsonRpcTesting::JsonRpcTestHarness {}; + +TEST_F(TestJsonCompliantJsonRpc, Ping) { + string response; + EXPECT_EQ(Core::ERROR_NONE, CallMethod("ping", R"({"payload":"abc"})", response)); + // impl echoes payload into reply; verify the value survives the compliant envelope + EXPECT_NE(response.find("abc"), string::npos); +} diff --git a/tests/FunctionalTests/jsonrpc/tests/TestJsonFormattingJsonRpc.cpp b/tests/FunctionalTests/jsonrpc/tests/TestJsonFormattingJsonRpc.cpp deleted file mode 100644 index e39891d9..00000000 --- a/tests/FunctionalTests/jsonrpc/tests/TestJsonFormattingJsonRpc.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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 "JsonRpcTestHarness.h" -#include -#include -#include -#include -#include - -using namespace Thunder; - -class TestJsonFormattingJsonRpc : public JsonRpcTesting::JsonRpcTestHarness {}; - -TEST_F(TestJsonFormattingJsonRpc, PingCompliant) { - string response; - EXPECT_EQ(Core::ERROR_NONE, CallMethod("ping", R"({"payload":"abc"})", response)); - EXPECT_FALSE(response.empty()); -} - -TEST_F(TestJsonFormattingJsonRpc, TextKeepNaming) { - string response; - EXPECT_EQ(Core::ERROR_NONE, - CallMethod("keep", R"({"InputValue":77})", response)); - EXPECT_FALSE(response.empty()); -} - -TEST_F(TestJsonFormattingJsonRpc, TextCaseNaming) { - string response; - EXPECT_EQ(Core::ERROR_NONE, - CallMethod("echocaseconvention", R"({"sourcevalue":123})", response)); - EXPECT_FALSE(response.empty()); -} - -TEST_F(TestJsonFormattingJsonRpc, UncompliantExtendedShape) { - string response; - EXPECT_EQ(Core::ERROR_NONE, - CallMethod("pingExtended", R"({"payload":"abc"})", response)); - EXPECT_FALSE(response.empty()); -} - -TEST_F(TestJsonFormattingJsonRpc, UncompliantCollapsedShape) { - string response; - EXPECT_EQ(Core::ERROR_NONE, - CallMethod("pingCollapsed", R"("abc")", response)); - EXPECT_FALSE(response.empty()); -} diff --git a/tests/FunctionalTests/jsonrpc/tests/TestJsonShapeJsonRpc.cpp b/tests/FunctionalTests/jsonrpc/tests/TestJsonShapeJsonRpc.cpp index efa87103..bf658529 100644 --- a/tests/FunctionalTests/jsonrpc/tests/TestJsonShapeJsonRpc.cpp +++ b/tests/FunctionalTests/jsonrpc/tests/TestJsonShapeJsonRpc.cpp @@ -28,19 +28,23 @@ class TestJsonShapeJsonRpc : public JsonRpcTesting::JsonRpcTestHarness {}; TEST_F(TestJsonShapeJsonRpc, GetWrappedCounter) { string response; EXPECT_EQ(Core::ERROR_NONE, CallMethod("getWrappedCounter", "{}", response)); - EXPECT_FALSE(response.empty()); + // impl counter is initialised to 42; @wrapped encloses it in an object envelope + EXPECT_NE(response.find("42"), string::npos); } TEST_F(TestJsonShapeJsonRpc, EchoExtractedList) { string response; EXPECT_EQ(Core::ERROR_NONE, CallMethod("echoExtractedList", R"({"input":[7]})", response)); - EXPECT_FALSE(response.empty()); + // verify the echoed list element survives the @extract round-trip + EXPECT_NE(response.find("7"), string::npos); } TEST_F(TestJsonShapeJsonRpc, EchoExtractedStruct) { string response; EXPECT_EQ(Core::ERROR_NONE, CallMethod("echoExtractedStruct", R"({"in":{"width":1920,"height":1080}})", response)); - EXPECT_FALSE(response.empty()); + // verify both struct fields survive the @extract round-trip + EXPECT_NE(response.find("1920"), string::npos); + EXPECT_NE(response.find("1080"), string::npos); } diff --git a/tests/FunctionalTests/jsonrpc/tests/TestLengthModesJsonRpc.cpp b/tests/FunctionalTests/jsonrpc/tests/TestJsonTextCaseJsonRpc.cpp similarity index 64% rename from tests/FunctionalTests/jsonrpc/tests/TestLengthModesJsonRpc.cpp rename to tests/FunctionalTests/jsonrpc/tests/TestJsonTextCaseJsonRpc.cpp index bb8c46a7..cae3415c 100644 --- a/tests/FunctionalTests/jsonrpc/tests/TestLengthModesJsonRpc.cpp +++ b/tests/FunctionalTests/jsonrpc/tests/TestJsonTextCaseJsonRpc.cpp @@ -19,22 +19,16 @@ #include #include "JsonRpcTestHarness.h" -#include +#include using namespace Thunder; -class TestLengthModesJsonRpc : public JsonRpcTesting::JsonRpcTestHarness {}; +class TestJsonTextCaseJsonRpc : public JsonRpcTesting::JsonRpcTestHarness {}; -TEST_F(TestLengthModesJsonRpc, EchoSingleByte) { +TEST_F(TestJsonTextCaseJsonRpc, EchoCaseConvention) { string response; EXPECT_EQ(Core::ERROR_NONE, - CallMethod("echoSingleByte", R"({"input":"Wg=="})", response)); - EXPECT_FALSE(response.empty()); -} - -TEST_F(TestLengthModesJsonRpc, ReadPayload) { - string response; - EXPECT_EQ(Core::ERROR_NONE, - CallMethod("readPayload", R"({"maxSize":16})", response)); - EXPECT_FALSE(response.empty()); + CallMethod("echocaseconvention", R"({"sourcevalue":123})", response)); + // @text:legacy lowercases method/param names; verify the echo-back value is correct + EXPECT_NE(response.find("123"), string::npos); } diff --git a/tests/FunctionalTests/jsonrpc/tests/TestJsonTextKeepJsonRpc.cpp b/tests/FunctionalTests/jsonrpc/tests/TestJsonTextKeepJsonRpc.cpp new file mode 100644 index 00000000..12d7588d --- /dev/null +++ b/tests/FunctionalTests/jsonrpc/tests/TestJsonTextKeepJsonRpc.cpp @@ -0,0 +1,42 @@ +/* + * 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 "JsonRpcTestHarness.h" +#include + +using namespace Thunder; + +class TestJsonTextKeepJsonRpc : public JsonRpcTesting::JsonRpcTestHarness {}; + +TEST_F(TestJsonTextKeepJsonRpc, EchoMixedCaseName) { + string response; + EXPECT_EQ(Core::ERROR_NONE, + CallMethod("EchoMixedCaseName", R"({"InputValue":77})", response)); + // @text:keep preserves exact C++ casing; verify the echo-back value is correct + EXPECT_NE(response.find("77"), string::npos); +} + +TEST_F(TestJsonTextKeepJsonRpc, BuildVersionProperty) { + string response; + EXPECT_EQ(Core::ERROR_NONE, + CallMethod("BuildVersion", "{}", response)); + // impl returns hardcoded version 1 + EXPECT_NE(response.find("1"), string::npos); +} diff --git a/tests/FunctionalTests/jsonrpc/tests/TestJsonUncompliantCollapsedJsonRpc.cpp b/tests/FunctionalTests/jsonrpc/tests/TestJsonUncompliantCollapsedJsonRpc.cpp new file mode 100644 index 00000000..f4693509 --- /dev/null +++ b/tests/FunctionalTests/jsonrpc/tests/TestJsonUncompliantCollapsedJsonRpc.cpp @@ -0,0 +1,34 @@ +/* + * 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 "JsonRpcTestHarness.h" +#include + +using namespace Thunder; + +class TestJsonUncompliantCollapsedJsonRpc : public JsonRpcTesting::JsonRpcTestHarness {}; + +TEST_F(TestJsonUncompliantCollapsedJsonRpc, PingCollapsed) { + string response; + EXPECT_EQ(Core::ERROR_NONE, + CallMethod("pingCollapsed", R"("abc")", response)); + // impl echoes payload; collapsed mode passes and returns raw JSON string + EXPECT_NE(response.find("abc"), string::npos); +} diff --git a/tests/FunctionalTests/jsonrpc/tests/TestJsonUncompliantExtendedJsonRpc.cpp b/tests/FunctionalTests/jsonrpc/tests/TestJsonUncompliantExtendedJsonRpc.cpp new file mode 100644 index 00000000..4aa2447c --- /dev/null +++ b/tests/FunctionalTests/jsonrpc/tests/TestJsonUncompliantExtendedJsonRpc.cpp @@ -0,0 +1,34 @@ +/* + * 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 "JsonRpcTestHarness.h" +#include + +using namespace Thunder; + +class TestJsonUncompliantExtendedJsonRpc : public JsonRpcTesting::JsonRpcTestHarness {}; + +TEST_F(TestJsonUncompliantExtendedJsonRpc, PingExtended) { + string response; + EXPECT_EQ(Core::ERROR_NONE, + CallMethod("pingExtended", R"({"payload":"abc"})", response)); + // impl echoes payload into reply; verify the value survives the extended uncompliant envelope + EXPECT_NE(response.find("abc"), string::npos); +} From f968e7d359eae766d8fa56199a2fca25190a34b8 Mon Sep 17 00:00:00 2001 From: Sankalp Maneshwar Date: Tue, 16 Jun 2026 13:57:08 +0530 Subject: [PATCH 07/29] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- tests/FunctionalTests/comrpc/tests/TestJsonCompliant.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/FunctionalTests/comrpc/tests/TestJsonCompliant.cpp b/tests/FunctionalTests/comrpc/tests/TestJsonCompliant.cpp index b34542ee..4ac02da8 100644 --- a/tests/FunctionalTests/comrpc/tests/TestJsonCompliant.cpp +++ b/tests/FunctionalTests/comrpc/tests/TestJsonCompliant.cpp @@ -29,4 +29,5 @@ class TestJsonCompliant : public Testing::TestHarness {}; TEST_F(TestJsonCompliant, Ping) { string reply; ASSERT_EQ(_proxy->Ping("payload", reply), Core::ERROR_NONE); + EXPECT_EQ(reply, "payload"); } From f6761ba81c1fe136f3e88065936d3d4dd8786791 Mon Sep 17 00:00:00 2001 From: Sankalp Maneshwar Date: Tue, 16 Jun 2026 13:57:21 +0530 Subject: [PATCH 08/29] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../FunctionalTests/comrpc/tests/TestJsonUncompliantExtended.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/FunctionalTests/comrpc/tests/TestJsonUncompliantExtended.cpp b/tests/FunctionalTests/comrpc/tests/TestJsonUncompliantExtended.cpp index d96f848e..2073d0a8 100644 --- a/tests/FunctionalTests/comrpc/tests/TestJsonUncompliantExtended.cpp +++ b/tests/FunctionalTests/comrpc/tests/TestJsonUncompliantExtended.cpp @@ -29,4 +29,5 @@ class TestJsonUncompliantExtended : public Testing::TestHarnessPingExtended("payload", reply), Core::ERROR_NONE); + EXPECT_EQ(reply, "payload"); } From 7e2a3803f410225825f7050728d3865d351aa2f7 Mon Sep 17 00:00:00 2001 From: Sankalp Maneshwar Date: Tue, 16 Jun 2026 13:57:31 +0530 Subject: [PATCH 09/29] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../comrpc/tests/TestJsonUncompliantCollapsed.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/FunctionalTests/comrpc/tests/TestJsonUncompliantCollapsed.cpp b/tests/FunctionalTests/comrpc/tests/TestJsonUncompliantCollapsed.cpp index c7cb5ef2..982749b8 100644 --- a/tests/FunctionalTests/comrpc/tests/TestJsonUncompliantCollapsed.cpp +++ b/tests/FunctionalTests/comrpc/tests/TestJsonUncompliantCollapsed.cpp @@ -29,4 +29,5 @@ class TestJsonUncompliantCollapsed : public Testing::TestHarnessPingCollapsed("payload", reply), Core::ERROR_NONE); + EXPECT_EQ(reply, "payload"); } From 7792415ea082cd8a60b57c28755e9011853f026a Mon Sep 17 00:00:00 2001 From: Sankalp Maneshwar Date: Tue, 16 Jun 2026 13:57:47 +0530 Subject: [PATCH 10/29] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- tests/FunctionalTests/comrpc/tests/TestJsonTextKeep.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/FunctionalTests/comrpc/tests/TestJsonTextKeep.cpp b/tests/FunctionalTests/comrpc/tests/TestJsonTextKeep.cpp index 7ff10df9..ac050174 100644 --- a/tests/FunctionalTests/comrpc/tests/TestJsonTextKeep.cpp +++ b/tests/FunctionalTests/comrpc/tests/TestJsonTextKeep.cpp @@ -35,4 +35,5 @@ TEST_F(TestJsonTextKeep, EchoMixedCaseName) { TEST_F(TestJsonTextKeep, BuildVersionProperty) { uint32_t version = 0; ASSERT_EQ(_proxy->BuildVersion(version), Core::ERROR_NONE); + EXPECT_EQ(version, 1u); } From 551f3f14b1960d0f543af66f334a70f873b24e30 Mon Sep 17 00:00:00 2001 From: Sankalp Maneshwar Date: Tue, 16 Jun 2026 13:58:03 +0530 Subject: [PATCH 11/29] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- tests/FunctionalTests/comrpc/tests/TestJsonShape.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/FunctionalTests/comrpc/tests/TestJsonShape.cpp b/tests/FunctionalTests/comrpc/tests/TestJsonShape.cpp index 38930937..832f872a 100644 --- a/tests/FunctionalTests/comrpc/tests/TestJsonShape.cpp +++ b/tests/FunctionalTests/comrpc/tests/TestJsonShape.cpp @@ -29,6 +29,7 @@ class TestJsonShape : public Testing::TestHarness {}; TEST_F(TestJsonShape, GetWrappedCounter) { uint32_t counter = 0; ASSERT_EQ(_proxy->GetWrappedCounter(counter), Core::ERROR_NONE); + EXPECT_EQ(counter, 42u); } TEST_F(TestJsonShape, EchoExtractedList) { From ef6b950c0826b90ab55e76f0b7b2dfb656664425 Mon Sep 17 00:00:00 2001 From: smanes0213 Date: Tue, 16 Jun 2026 14:22:44 +0530 Subject: [PATCH 12/29] Resolve review comments --- tests/FunctionalTests/README.md | 8 ++++---- .../common/implementations/TestJsonShapeImpl.cpp | 2 +- tests/FunctionalTests/common/interfaces/ITestJsonShape.h | 2 +- tests/FunctionalTests/comrpc/tests/TestJsonShape.cpp | 4 ++-- tests/FunctionalTests/comrpc/tests/TestLengthModes.cpp | 6 ++++++ .../jsonrpc/tests/TestJsonShapeJsonRpc.cpp | 6 +++--- .../jsonrpc/tests/TestJsonTextKeepJsonRpc.cpp | 4 ++-- 7 files changed, 19 insertions(+), 13 deletions(-) diff --git a/tests/FunctionalTests/README.md b/tests/FunctionalTests/README.md index e3bffa6c..c7903953 100644 --- a/tests/FunctionalTests/README.md +++ b/tests/FunctionalTests/README.md @@ -60,7 +60,7 @@ This generates JSON-RPC dispatch code but integration with the main test executa ### JSON-RPC functional tests -A separate test executable validates JsonGenerator output using a simplified header-only architecture: +A separate test executable validates JsonGenerator output using a lightweight two-file server infrastructure: ```bash cmake -S . -B build -DBUILD_JSON_RPC_TESTS=ON @@ -71,7 +71,7 @@ cmake --build build --target JsonRpcFunctionalTests This creates `JsonRpcFunctionalTests` executable that: - Reuses the same interface definitions and implementation classes as COM-RPC tests - Tests **full round-trip JSON marshalling/unmarshalling** using Thunder's `PluginHost::JSONRPC::Invoke()` directly -- Uses **header-only infrastructure** (`JsonRpcServer.h` contains both registration system and dispatcher) +- Uses a lightweight two-file infrastructure: `JsonRpcServer.h` contains the registration system and dispatcher, `JsonRpcServer.cpp` contains the runtime bootstrap (`ThunderTestRuntime` init and `IShell` attach) - **One-line registration** per interface using lambda-based static registrars (no template specialization boilerplate) - Uses `JsonRpcTestHarness` fixtures instead of `TestHarness` - Validates that generated `J::Register()` dispatch code compiles and works correctly @@ -140,7 +140,7 @@ ProxyStubs are generated by `ProxyStubGenerator` at build time into `build/comrp ## JSON-RPC test architecture -The JSON-RPC test binary performs direct in-process dispatcher invocation (no network transport). `JsonRpcServer` is built on `Test::JsonRPCRegister` (from `JsonRpcRegistrations.h`), which derives from `PluginHost::JSONRPCSupportsEventStatus`. The infrastructure is **header-only** - all registration and dispatch logic lives in `JsonRpcServer.h`: +The JSON-RPC test binary performs direct in-process dispatcher invocation (no network transport). `JsonRpcServer` is built on `Test::JsonRPCRegister` (from `JsonRpcRegistrations.h`), which derives from `PluginHost::JSONRPCSupportsEventStatus`. The infrastructure splits across two files: `JsonRpcServer.h` contains the registration system and dispatcher, while `JsonRpcServer.cpp` contains the runtime bootstrap (`ThunderTestRuntime` initialisation and `IShell` attachment): ``` ┌────────────────────────────────────────────────────┐ @@ -170,7 +170,7 @@ The JSON-RPC test binary performs direct in-process dispatcher invocation (no ne **Server side** — `JsonRpcServer` derives from `Test::JsonRPCRegister` (which derives from `PluginHost::JSONRPCSupportsEventStatus`) and does not implement `PluginHost::IPlugin`. It initializes `Thunder::TestCore::ThunderTestRuntime` and attaches to a real `IShell` from the embedded Thunder `PluginHost::Server` (Controller callsign). Implementation classes are the same protocol-agnostic implementations used for COM-RPC tests. -**Registration system** (header-only in `JsonRpcServer.h`): +**Registration system** (in `JsonRpcServer.h`): - `JsonRpcRegistrationProvider`: Singleton collecting registration lambdas - `JsonRpcRegistrar`: Template class capturing register function in lambda - Static initialization: Each implementation file has one-line registrar: diff --git a/tests/FunctionalTests/common/implementations/TestJsonShapeImpl.cpp b/tests/FunctionalTests/common/implementations/TestJsonShapeImpl.cpp index 737faee8..f0fbe17d 100644 --- a/tests/FunctionalTests/common/implementations/TestJsonShapeImpl.cpp +++ b/tests/FunctionalTests/common/implementations/TestJsonShapeImpl.cpp @@ -45,7 +45,7 @@ namespace TestImplementation { return Core::ERROR_NONE; } - Core::hresult EchoExtractedStruct( + Core::hresult EchoStruct( const Dimensions& in, Dimensions& out) const override { diff --git a/tests/FunctionalTests/common/interfaces/ITestJsonShape.h b/tests/FunctionalTests/common/interfaces/ITestJsonShape.h index 3ed3acf2..b3e1e99a 100644 --- a/tests/FunctionalTests/common/interfaces/ITestJsonShape.h +++ b/tests/FunctionalTests/common/interfaces/ITestJsonShape.h @@ -58,7 +58,7 @@ namespace FunctionalTest { // @param in Input Dimensions value. // @param out Receives echoed Dimensions value. // @retval ERROR_NONE Echo completed. - virtual Core::hresult EchoExtractedStruct( + virtual Core::hresult EchoStruct( const Dimensions& in /* @in */, Dimensions& out /* @out */) const = 0; }; diff --git a/tests/FunctionalTests/comrpc/tests/TestJsonShape.cpp b/tests/FunctionalTests/comrpc/tests/TestJsonShape.cpp index 832f872a..6035a3ac 100644 --- a/tests/FunctionalTests/comrpc/tests/TestJsonShape.cpp +++ b/tests/FunctionalTests/comrpc/tests/TestJsonShape.cpp @@ -41,11 +41,11 @@ TEST_F(TestJsonShape, EchoExtractedList) { EXPECT_EQ(output[0], input[0]); } -TEST_F(TestJsonShape, EchoExtractedStruct) { +TEST_F(TestJsonShape, EchoStruct) { const ITestJsonShape::Dimensions input { 1920, 1080 }; ITestJsonShape::Dimensions output { 0, 0 }; - ASSERT_EQ(_proxy->EchoExtractedStruct(input, output), Core::ERROR_NONE); + ASSERT_EQ(_proxy->EchoStruct(input, output), Core::ERROR_NONE); EXPECT_EQ(output.width, input.width); EXPECT_EQ(output.height, input.height); } diff --git a/tests/FunctionalTests/comrpc/tests/TestLengthModes.cpp b/tests/FunctionalTests/comrpc/tests/TestLengthModes.cpp index efea1c88..77b96ad3 100644 --- a/tests/FunctionalTests/comrpc/tests/TestLengthModes.cpp +++ b/tests/FunctionalTests/comrpc/tests/TestLengthModes.cpp @@ -39,4 +39,10 @@ TEST_F(TestLengthModes, ReadPayloadHonorsCapacity) { const uint16_t written = _proxy->ReadPayload(output, static_cast(sizeof(output))); EXPECT_LE(written, static_cast(sizeof(output))); + // impl writes a fixed 4-byte pattern {0xDE, 0xAD, 0xBE, 0xEF}; verify bytes were actually written + EXPECT_GT(written, 0u); + EXPECT_EQ(output[0], 0xDE); + EXPECT_EQ(output[1], 0xAD); + EXPECT_EQ(output[2], 0xBE); + EXPECT_EQ(output[3], 0xEF); } diff --git a/tests/FunctionalTests/jsonrpc/tests/TestJsonShapeJsonRpc.cpp b/tests/FunctionalTests/jsonrpc/tests/TestJsonShapeJsonRpc.cpp index bf658529..3546a425 100644 --- a/tests/FunctionalTests/jsonrpc/tests/TestJsonShapeJsonRpc.cpp +++ b/tests/FunctionalTests/jsonrpc/tests/TestJsonShapeJsonRpc.cpp @@ -40,11 +40,11 @@ TEST_F(TestJsonShapeJsonRpc, EchoExtractedList) { EXPECT_NE(response.find("7"), string::npos); } -TEST_F(TestJsonShapeJsonRpc, EchoExtractedStruct) { +TEST_F(TestJsonShapeJsonRpc, EchoStruct) { string response; EXPECT_EQ(Core::ERROR_NONE, - CallMethod("echoExtractedStruct", R"({"in":{"width":1920,"height":1080}})", response)); - // verify both struct fields survive the @extract round-trip + CallMethod("echoStruct", R"({"in":{"width":1920,"height":1080}})", response)); + // verify both struct fields survive the standard (non-extracted) round-trip EXPECT_NE(response.find("1920"), string::npos); EXPECT_NE(response.find("1080"), string::npos); } diff --git a/tests/FunctionalTests/jsonrpc/tests/TestJsonTextKeepJsonRpc.cpp b/tests/FunctionalTests/jsonrpc/tests/TestJsonTextKeepJsonRpc.cpp index 12d7588d..5facff62 100644 --- a/tests/FunctionalTests/jsonrpc/tests/TestJsonTextKeepJsonRpc.cpp +++ b/tests/FunctionalTests/jsonrpc/tests/TestJsonTextKeepJsonRpc.cpp @@ -37,6 +37,6 @@ TEST_F(TestJsonTextKeepJsonRpc, BuildVersionProperty) { string response; EXPECT_EQ(Core::ERROR_NONE, CallMethod("BuildVersion", "{}", response)); - // impl returns hardcoded version 1 - EXPECT_NE(response.find("1"), string::npos); + // impl returns hardcoded version 1; match the field name to avoid false-positive on "id":1 + EXPECT_NE(response.find("\"BuildVersion\":1"), string::npos); } From 76794b636540042639b5879b94894f8fc0644864 Mon Sep 17 00:00:00 2001 From: smanes0213 Date: Tue, 16 Jun 2026 14:34:55 +0530 Subject: [PATCH 13/29] Resolve test failure --- .../FunctionalTests/jsonrpc/tests/TestJsonTextKeepJsonRpc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/FunctionalTests/jsonrpc/tests/TestJsonTextKeepJsonRpc.cpp b/tests/FunctionalTests/jsonrpc/tests/TestJsonTextKeepJsonRpc.cpp index 5facff62..ef4706f0 100644 --- a/tests/FunctionalTests/jsonrpc/tests/TestJsonTextKeepJsonRpc.cpp +++ b/tests/FunctionalTests/jsonrpc/tests/TestJsonTextKeepJsonRpc.cpp @@ -37,6 +37,6 @@ TEST_F(TestJsonTextKeepJsonRpc, BuildVersionProperty) { string response; EXPECT_EQ(Core::ERROR_NONE, CallMethod("BuildVersion", "{}", response)); - // impl returns hardcoded version 1; match the field name to avoid false-positive on "id":1 - EXPECT_NE(response.find("\"BuildVersion\":1"), string::npos); + // @property getters return the value directly as "result": in Thunder JSON-RPC + EXPECT_NE(response.find("\"result\":1"), string::npos); } From e6ec66de2d009c13d84c2c90f32dcc5a6ad4cc29 Mon Sep 17 00:00:00 2001 From: smanes0213 Date: Tue, 16 Jun 2026 14:46:55 +0530 Subject: [PATCH 14/29] Resolve test failure --- .../common/implementations/TestJsonTextKeepImpl.cpp | 2 +- tests/FunctionalTests/comrpc/tests/TestJsonTextKeep.cpp | 2 +- .../FunctionalTests/jsonrpc/tests/TestJsonTextKeepJsonRpc.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/FunctionalTests/common/implementations/TestJsonTextKeepImpl.cpp b/tests/FunctionalTests/common/implementations/TestJsonTextKeepImpl.cpp index 1681409d..8561c97a 100644 --- a/tests/FunctionalTests/common/implementations/TestJsonTextKeepImpl.cpp +++ b/tests/FunctionalTests/common/implementations/TestJsonTextKeepImpl.cpp @@ -25,7 +25,7 @@ namespace TestImplementation { class TestJsonTextKeepImpl : public FunctionalTest::ITestJsonTextKeep { public: - TestJsonTextKeepImpl() : _buildVersion(1) {} + TestJsonTextKeepImpl() : _buildVersion(42) {} ~TestJsonTextKeepImpl() override = default; TestJsonTextKeepImpl(const TestJsonTextKeepImpl&) = delete; diff --git a/tests/FunctionalTests/comrpc/tests/TestJsonTextKeep.cpp b/tests/FunctionalTests/comrpc/tests/TestJsonTextKeep.cpp index ac050174..afcea24e 100644 --- a/tests/FunctionalTests/comrpc/tests/TestJsonTextKeep.cpp +++ b/tests/FunctionalTests/comrpc/tests/TestJsonTextKeep.cpp @@ -35,5 +35,5 @@ TEST_F(TestJsonTextKeep, EchoMixedCaseName) { TEST_F(TestJsonTextKeep, BuildVersionProperty) { uint32_t version = 0; ASSERT_EQ(_proxy->BuildVersion(version), Core::ERROR_NONE); - EXPECT_EQ(version, 1u); + EXPECT_EQ(version, 42u); } diff --git a/tests/FunctionalTests/jsonrpc/tests/TestJsonTextKeepJsonRpc.cpp b/tests/FunctionalTests/jsonrpc/tests/TestJsonTextKeepJsonRpc.cpp index ef4706f0..376ea04c 100644 --- a/tests/FunctionalTests/jsonrpc/tests/TestJsonTextKeepJsonRpc.cpp +++ b/tests/FunctionalTests/jsonrpc/tests/TestJsonTextKeepJsonRpc.cpp @@ -37,6 +37,6 @@ TEST_F(TestJsonTextKeepJsonRpc, BuildVersionProperty) { string response; EXPECT_EQ(Core::ERROR_NONE, CallMethod("BuildVersion", "{}", response)); - // @property getters return the value directly as "result": in Thunder JSON-RPC - EXPECT_NE(response.find("\"result\":1"), string::npos); + // impl returns hardcoded version 42; search for the bare number (won't appear in the JSON-RPC envelope) + EXPECT_NE(response.find("42"), string::npos); } From 74c74fce602d762ab7cf13ec29f2fc888496ffcd Mon Sep 17 00:00:00 2001 From: smanes0213 Date: Wed, 17 Jun 2026 12:20:54 +0530 Subject: [PATCH 15/29] Add Jsonrpc functional tests --- tests/FunctionalTests/jsonrpc/CMakeLists.txt | 4 + .../jsonrpc/tests/TestAsyncJsonRpc.cpp | 122 ++++++++++++++++++ .../jsonrpc/tests/TestStructsJsonRpc.cpp | 30 +++++ 3 files changed, 156 insertions(+) create mode 100644 tests/FunctionalTests/jsonrpc/tests/TestAsyncJsonRpc.cpp diff --git a/tests/FunctionalTests/jsonrpc/CMakeLists.txt b/tests/FunctionalTests/jsonrpc/CMakeLists.txt index f09e42a5..1bfe804a 100644 --- a/tests/FunctionalTests/jsonrpc/CMakeLists.txt +++ b/tests/FunctionalTests/jsonrpc/CMakeLists.txt @@ -25,6 +25,10 @@ target_include_directories(JsonRpcFunctionalTests ${CMAKE_CURRENT_SOURCE_DIR} ) +if(TEST_ASYNC) + target_sources(JsonRpcFunctionalTests PRIVATE tests/TestAsyncJsonRpc.cpp) +endif() + if(TEST_ENUMS) target_sources(JsonRpcFunctionalTests PRIVATE tests/TestEnumsJsonRpc.cpp) endif() diff --git a/tests/FunctionalTests/jsonrpc/tests/TestAsyncJsonRpc.cpp b/tests/FunctionalTests/jsonrpc/tests/TestAsyncJsonRpc.cpp new file mode 100644 index 00000000..869d8951 --- /dev/null +++ b/tests/FunctionalTests/jsonrpc/tests/TestAsyncJsonRpc.cpp @@ -0,0 +1,122 @@ +/* + * 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. + */ + +// Tests the following annotation tags via ITestAsync in JSON-RPC mode: +// +// @async — Calculate() maps to a non-blocking JSON-RPC call that returns a +// slot index synchronously; completion is delivered via a JSON-RPC +// event. The ICallback* parameter is replaced by event machinery; +// callers poll SlotResult or subscribe to the completion event. +// +// @default — delayMs carries @default:100; omitting it from the request body +// should succeed (the generator substitutes the default value). +// +// @index — SlotResult is an @index @property; each slot is independently +// addressable as slotResult@. + +#include +#include "JsonRpcTestHarness.h" +#include +#include +#include + +using namespace Thunder; + +class TestAsyncJsonRpc : public JsonRpcTesting::JsonRpcTestHarness {}; + +// Helper: parse the slot number from a calculate response that contains "slot":. +// Returns 255 and marks the test failed if the field is not found. +static uint8_t ParseSlot(const string& response) +{ + auto pos = response.find("\"slot\":"); + if (pos == string::npos) { + ADD_FAILURE() << "No \"slot\" field in calculate response: " << response; + return 255; + } + return static_cast(std::stoi(response.substr(pos + 7))); +} + +// @async — calculate() returns a slot index synchronously in the JSON-RPC response; +// the ICallback is replaced by the framework's event-delivery mechanism. +TEST_F(TestAsyncJsonRpc, Calculate_ReturnsSlot) +{ + string response; + ASSERT_EQ(Core::ERROR_NONE, + CallMethod("calculate", R"({"value":21,"delayMs":50})", response)); + uint8_t slot = ParseSlot(response); + ASSERT_LE(slot, 6u) << "Slot must be in range [0..6]: " << response; + + // Abort the running calculation so the slot does not leak into other tests. + CallMethod("abort", R"({"slot":)" + std::to_string(slot) + "}", response); +} + +// @default — omitting delayMs should apply the @default:100 annotation value +// and the call must still succeed. +TEST_F(TestAsyncJsonRpc, Calculate_DefaultDelay) +{ + string response; + ASSERT_EQ(Core::ERROR_NONE, + CallMethod("calculate", R"({"value":10})", response)); + uint8_t slot = ParseSlot(response); + ASSERT_LE(slot, 6u) << "Slot must be in range [0..6]: " << response; + + // Wait for the default-delay (100 ms) calculation to complete, then consume + // the result so the slot is freed before the next test. + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + CallMethod("slotResult@" + std::to_string(slot), "{}", response); +} + +// @async state — inProgress must report true while the calculation is still running. +TEST_F(TestAsyncJsonRpc, InProgress_WhileRunning) +{ + string response; + ASSERT_EQ(Core::ERROR_NONE, + CallMethod("calculate", R"({"value":1,"delayMs":500})", response)); + uint8_t slot = ParseSlot(response); + ASSERT_LE(slot, 6u); + + string ipResponse; + ASSERT_EQ(Core::ERROR_NONE, + CallMethod("inProgress", R"({"slot":)" + std::to_string(slot) + "}", ipResponse)); + EXPECT_NE(ipResponse.find("true"), string::npos) << "Response: " << ipResponse; + + // Abort so the slow calculation does not run for the full 500 ms. + CallMethod("abort", R"({"slot":)" + std::to_string(slot) + "}", response); +} + +// @index — SlotResult is an @index @property; after the calculation completes +// the result is read via the indexed accessor slotResult@. +TEST_F(TestAsyncJsonRpc, SlotResult_IndexProperty) +{ + string response; + ASSERT_EQ(Core::ERROR_NONE, + CallMethod("calculate", R"({"value":21,"delayMs":50})", response)); + uint8_t slot = ParseSlot(response); + ASSERT_LE(slot, 6u); + + // Wait for the 50 ms calculation to finish. + std::this_thread::sleep_for(std::chrono::milliseconds(300)); + + // Read the result via the @index property: slotResult@ + string resultResponse; + ASSERT_EQ(Core::ERROR_NONE, + CallMethod("slotResult@" + std::to_string(slot), "{}", resultResponse)); + // impl computes value * 2, so 21 * 2 = 42 + EXPECT_NE(resultResponse.find("42"), string::npos) << "Response: " << resultResponse; +} diff --git a/tests/FunctionalTests/jsonrpc/tests/TestStructsJsonRpc.cpp b/tests/FunctionalTests/jsonrpc/tests/TestStructsJsonRpc.cpp index a1e06cb1..def9cd35 100644 --- a/tests/FunctionalTests/jsonrpc/tests/TestStructsJsonRpc.cpp +++ b/tests/FunctionalTests/jsonrpc/tests/TestStructsJsonRpc.cpp @@ -154,3 +154,33 @@ TEST_F(TestStructsJsonRpc, IsValidRectangle) { EXPECT_NE(response.find("true"), string::npos) << "Response: " << response; } +// ===== @opaque — config property passes its JSON blob through without deserialisation ===== + +TEST_F(TestStructsJsonRpc, Config_Opaque_RoundTrip) { + string response; + // SET — the opaque blob is passed as the raw params object + EXPECT_EQ(Core::ERROR_NONE, + CallMethod("config", R"({"level":5,"label":"test"})", response)); + + response.clear(); + // GET — the stored blob is returned verbatim + EXPECT_EQ(Core::ERROR_NONE, CallMethod("config", "{}", response)); + EXPECT_NE(response.find("level"), string::npos) << "Response: " << response; + EXPECT_NE(response.find("test"), string::npos) << "Response: " << response; +} + +// ===== @index — slotPoint@ addresses each slot independently ===== + +TEST_F(TestStructsJsonRpc, SlotPoint_IndexedProperty) { + string response; + // SET slotPoint@0 — @index makes the slot suffix part of the method name + EXPECT_EQ(Core::ERROR_NONE, + CallMethod("slotPoint@0", R"({"x":7,"y":13})", response)); + + response.clear(); + // GET slotPoint@0 + EXPECT_EQ(Core::ERROR_NONE, CallMethod("slotPoint@0", "{}", response)); + EXPECT_NE(response.find("7"), string::npos) << "Response: " << response; + EXPECT_NE(response.find("13"), string::npos) << "Response: " << response; +} + From 522a4c971126575b22e33987ed2b7b8c372bff61 Mon Sep 17 00:00:00 2001 From: smanes0213 Date: Wed, 17 Jun 2026 12:27:54 +0530 Subject: [PATCH 16/29] Resolve build failure --- tests/FunctionalTests/common/CMakeLists.txt | 2 +- tests/FunctionalTests/jsonrpc/CMakeLists.txt | 4 - .../jsonrpc/tests/TestAsyncJsonRpc.cpp | 122 ------------------ 3 files changed, 1 insertion(+), 127 deletions(-) delete mode 100644 tests/FunctionalTests/jsonrpc/tests/TestAsyncJsonRpc.cpp diff --git a/tests/FunctionalTests/common/CMakeLists.txt b/tests/FunctionalTests/common/CMakeLists.txt index ef4ca733..a780747e 100644 --- a/tests/FunctionalTests/common/CMakeLists.txt +++ b/tests/FunctionalTests/common/CMakeLists.txt @@ -152,7 +152,7 @@ if (TEST_EVENTS) endif() if (TEST_ASYNC) - AddTestInterface("Async" COM_RPC JSON_RPC) + AddTestInterface("Async" COM_RPC) endif() if (TEST_ENCODING_MAC) diff --git a/tests/FunctionalTests/jsonrpc/CMakeLists.txt b/tests/FunctionalTests/jsonrpc/CMakeLists.txt index 1bfe804a..f09e42a5 100644 --- a/tests/FunctionalTests/jsonrpc/CMakeLists.txt +++ b/tests/FunctionalTests/jsonrpc/CMakeLists.txt @@ -25,10 +25,6 @@ target_include_directories(JsonRpcFunctionalTests ${CMAKE_CURRENT_SOURCE_DIR} ) -if(TEST_ASYNC) - target_sources(JsonRpcFunctionalTests PRIVATE tests/TestAsyncJsonRpc.cpp) -endif() - if(TEST_ENUMS) target_sources(JsonRpcFunctionalTests PRIVATE tests/TestEnumsJsonRpc.cpp) endif() diff --git a/tests/FunctionalTests/jsonrpc/tests/TestAsyncJsonRpc.cpp b/tests/FunctionalTests/jsonrpc/tests/TestAsyncJsonRpc.cpp deleted file mode 100644 index 869d8951..00000000 --- a/tests/FunctionalTests/jsonrpc/tests/TestAsyncJsonRpc.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/* - * 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. - */ - -// Tests the following annotation tags via ITestAsync in JSON-RPC mode: -// -// @async — Calculate() maps to a non-blocking JSON-RPC call that returns a -// slot index synchronously; completion is delivered via a JSON-RPC -// event. The ICallback* parameter is replaced by event machinery; -// callers poll SlotResult or subscribe to the completion event. -// -// @default — delayMs carries @default:100; omitting it from the request body -// should succeed (the generator substitutes the default value). -// -// @index — SlotResult is an @index @property; each slot is independently -// addressable as slotResult@. - -#include -#include "JsonRpcTestHarness.h" -#include -#include -#include - -using namespace Thunder; - -class TestAsyncJsonRpc : public JsonRpcTesting::JsonRpcTestHarness {}; - -// Helper: parse the slot number from a calculate response that contains "slot":. -// Returns 255 and marks the test failed if the field is not found. -static uint8_t ParseSlot(const string& response) -{ - auto pos = response.find("\"slot\":"); - if (pos == string::npos) { - ADD_FAILURE() << "No \"slot\" field in calculate response: " << response; - return 255; - } - return static_cast(std::stoi(response.substr(pos + 7))); -} - -// @async — calculate() returns a slot index synchronously in the JSON-RPC response; -// the ICallback is replaced by the framework's event-delivery mechanism. -TEST_F(TestAsyncJsonRpc, Calculate_ReturnsSlot) -{ - string response; - ASSERT_EQ(Core::ERROR_NONE, - CallMethod("calculate", R"({"value":21,"delayMs":50})", response)); - uint8_t slot = ParseSlot(response); - ASSERT_LE(slot, 6u) << "Slot must be in range [0..6]: " << response; - - // Abort the running calculation so the slot does not leak into other tests. - CallMethod("abort", R"({"slot":)" + std::to_string(slot) + "}", response); -} - -// @default — omitting delayMs should apply the @default:100 annotation value -// and the call must still succeed. -TEST_F(TestAsyncJsonRpc, Calculate_DefaultDelay) -{ - string response; - ASSERT_EQ(Core::ERROR_NONE, - CallMethod("calculate", R"({"value":10})", response)); - uint8_t slot = ParseSlot(response); - ASSERT_LE(slot, 6u) << "Slot must be in range [0..6]: " << response; - - // Wait for the default-delay (100 ms) calculation to complete, then consume - // the result so the slot is freed before the next test. - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - CallMethod("slotResult@" + std::to_string(slot), "{}", response); -} - -// @async state — inProgress must report true while the calculation is still running. -TEST_F(TestAsyncJsonRpc, InProgress_WhileRunning) -{ - string response; - ASSERT_EQ(Core::ERROR_NONE, - CallMethod("calculate", R"({"value":1,"delayMs":500})", response)); - uint8_t slot = ParseSlot(response); - ASSERT_LE(slot, 6u); - - string ipResponse; - ASSERT_EQ(Core::ERROR_NONE, - CallMethod("inProgress", R"({"slot":)" + std::to_string(slot) + "}", ipResponse)); - EXPECT_NE(ipResponse.find("true"), string::npos) << "Response: " << ipResponse; - - // Abort so the slow calculation does not run for the full 500 ms. - CallMethod("abort", R"({"slot":)" + std::to_string(slot) + "}", response); -} - -// @index — SlotResult is an @index @property; after the calculation completes -// the result is read via the indexed accessor slotResult@. -TEST_F(TestAsyncJsonRpc, SlotResult_IndexProperty) -{ - string response; - ASSERT_EQ(Core::ERROR_NONE, - CallMethod("calculate", R"({"value":21,"delayMs":50})", response)); - uint8_t slot = ParseSlot(response); - ASSERT_LE(slot, 6u); - - // Wait for the 50 ms calculation to finish. - std::this_thread::sleep_for(std::chrono::milliseconds(300)); - - // Read the result via the @index property: slotResult@ - string resultResponse; - ASSERT_EQ(Core::ERROR_NONE, - CallMethod("slotResult@" + std::to_string(slot), "{}", resultResponse)); - // impl computes value * 2, so 21 * 2 = 42 - EXPECT_NE(resultResponse.find("42"), string::npos) << "Response: " << resultResponse; -} From 8f0880e3e4de1483f8030b4346d85925842296d6 Mon Sep 17 00:00:00 2001 From: smanes0213 Date: Wed, 17 Jun 2026 12:34:47 +0530 Subject: [PATCH 17/29] Resolve build failure --- tests/FunctionalTests/common/CMakeLists.txt | 3 +- tests/FunctionalTests/jsonrpc/CMakeLists.txt | 4 + .../jsonrpc/tests/TestAsyncJsonRpc.cpp | 122 ++++++++++++++++++ 3 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 tests/FunctionalTests/jsonrpc/tests/TestAsyncJsonRpc.cpp diff --git a/tests/FunctionalTests/common/CMakeLists.txt b/tests/FunctionalTests/common/CMakeLists.txt index a780747e..b1d089de 100644 --- a/tests/FunctionalTests/common/CMakeLists.txt +++ b/tests/FunctionalTests/common/CMakeLists.txt @@ -65,6 +65,7 @@ target_link_libraries(FunctionalTestJsonSources PRIVATE FunctionalTestCommon ${NAMESPACE}Core::${NAMESPACE}Core + ${NAMESPACE}Plugins::${NAMESPACE}Plugins ) macro(AddTestInterface TestName) @@ -152,7 +153,7 @@ if (TEST_EVENTS) endif() if (TEST_ASYNC) - AddTestInterface("Async" COM_RPC) + AddTestInterface("Async" COM_RPC JSON_RPC) endif() if (TEST_ENCODING_MAC) diff --git a/tests/FunctionalTests/jsonrpc/CMakeLists.txt b/tests/FunctionalTests/jsonrpc/CMakeLists.txt index f09e42a5..1bfe804a 100644 --- a/tests/FunctionalTests/jsonrpc/CMakeLists.txt +++ b/tests/FunctionalTests/jsonrpc/CMakeLists.txt @@ -25,6 +25,10 @@ target_include_directories(JsonRpcFunctionalTests ${CMAKE_CURRENT_SOURCE_DIR} ) +if(TEST_ASYNC) + target_sources(JsonRpcFunctionalTests PRIVATE tests/TestAsyncJsonRpc.cpp) +endif() + if(TEST_ENUMS) target_sources(JsonRpcFunctionalTests PRIVATE tests/TestEnumsJsonRpc.cpp) endif() diff --git a/tests/FunctionalTests/jsonrpc/tests/TestAsyncJsonRpc.cpp b/tests/FunctionalTests/jsonrpc/tests/TestAsyncJsonRpc.cpp new file mode 100644 index 00000000..869d8951 --- /dev/null +++ b/tests/FunctionalTests/jsonrpc/tests/TestAsyncJsonRpc.cpp @@ -0,0 +1,122 @@ +/* + * 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. + */ + +// Tests the following annotation tags via ITestAsync in JSON-RPC mode: +// +// @async — Calculate() maps to a non-blocking JSON-RPC call that returns a +// slot index synchronously; completion is delivered via a JSON-RPC +// event. The ICallback* parameter is replaced by event machinery; +// callers poll SlotResult or subscribe to the completion event. +// +// @default — delayMs carries @default:100; omitting it from the request body +// should succeed (the generator substitutes the default value). +// +// @index — SlotResult is an @index @property; each slot is independently +// addressable as slotResult@. + +#include +#include "JsonRpcTestHarness.h" +#include +#include +#include + +using namespace Thunder; + +class TestAsyncJsonRpc : public JsonRpcTesting::JsonRpcTestHarness {}; + +// Helper: parse the slot number from a calculate response that contains "slot":. +// Returns 255 and marks the test failed if the field is not found. +static uint8_t ParseSlot(const string& response) +{ + auto pos = response.find("\"slot\":"); + if (pos == string::npos) { + ADD_FAILURE() << "No \"slot\" field in calculate response: " << response; + return 255; + } + return static_cast(std::stoi(response.substr(pos + 7))); +} + +// @async — calculate() returns a slot index synchronously in the JSON-RPC response; +// the ICallback is replaced by the framework's event-delivery mechanism. +TEST_F(TestAsyncJsonRpc, Calculate_ReturnsSlot) +{ + string response; + ASSERT_EQ(Core::ERROR_NONE, + CallMethod("calculate", R"({"value":21,"delayMs":50})", response)); + uint8_t slot = ParseSlot(response); + ASSERT_LE(slot, 6u) << "Slot must be in range [0..6]: " << response; + + // Abort the running calculation so the slot does not leak into other tests. + CallMethod("abort", R"({"slot":)" + std::to_string(slot) + "}", response); +} + +// @default — omitting delayMs should apply the @default:100 annotation value +// and the call must still succeed. +TEST_F(TestAsyncJsonRpc, Calculate_DefaultDelay) +{ + string response; + ASSERT_EQ(Core::ERROR_NONE, + CallMethod("calculate", R"({"value":10})", response)); + uint8_t slot = ParseSlot(response); + ASSERT_LE(slot, 6u) << "Slot must be in range [0..6]: " << response; + + // Wait for the default-delay (100 ms) calculation to complete, then consume + // the result so the slot is freed before the next test. + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + CallMethod("slotResult@" + std::to_string(slot), "{}", response); +} + +// @async state — inProgress must report true while the calculation is still running. +TEST_F(TestAsyncJsonRpc, InProgress_WhileRunning) +{ + string response; + ASSERT_EQ(Core::ERROR_NONE, + CallMethod("calculate", R"({"value":1,"delayMs":500})", response)); + uint8_t slot = ParseSlot(response); + ASSERT_LE(slot, 6u); + + string ipResponse; + ASSERT_EQ(Core::ERROR_NONE, + CallMethod("inProgress", R"({"slot":)" + std::to_string(slot) + "}", ipResponse)); + EXPECT_NE(ipResponse.find("true"), string::npos) << "Response: " << ipResponse; + + // Abort so the slow calculation does not run for the full 500 ms. + CallMethod("abort", R"({"slot":)" + std::to_string(slot) + "}", response); +} + +// @index — SlotResult is an @index @property; after the calculation completes +// the result is read via the indexed accessor slotResult@. +TEST_F(TestAsyncJsonRpc, SlotResult_IndexProperty) +{ + string response; + ASSERT_EQ(Core::ERROR_NONE, + CallMethod("calculate", R"({"value":21,"delayMs":50})", response)); + uint8_t slot = ParseSlot(response); + ASSERT_LE(slot, 6u); + + // Wait for the 50 ms calculation to finish. + std::this_thread::sleep_for(std::chrono::milliseconds(300)); + + // Read the result via the @index property: slotResult@ + string resultResponse; + ASSERT_EQ(Core::ERROR_NONE, + CallMethod("slotResult@" + std::to_string(slot), "{}", resultResponse)); + // impl computes value * 2, so 21 * 2 = 42 + EXPECT_NE(resultResponse.find("42"), string::npos) << "Response: " << resultResponse; +} From 9d9654745c879760e20e03a5ab8e3d5e92b297b1 Mon Sep 17 00:00:00 2001 From: smanes0213 Date: Wed, 17 Jun 2026 14:01:14 +0530 Subject: [PATCH 18/29] Update the include path for JSONRPC.h --- tests/FunctionalTests/common/include/JsonRpcRegistrations.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/FunctionalTests/common/include/JsonRpcRegistrations.h b/tests/FunctionalTests/common/include/JsonRpcRegistrations.h index 7b140433..3a5195e4 100644 --- a/tests/FunctionalTests/common/include/JsonRpcRegistrations.h +++ b/tests/FunctionalTests/common/include/JsonRpcRegistrations.h @@ -19,7 +19,7 @@ #pragma once -#include +#include namespace Thunder { namespace Test { From 31752531ade214b7132a29f125e0750bde6af98d Mon Sep 17 00:00:00 2001 From: smanes0213 Date: Wed, 17 Jun 2026 14:06:42 +0530 Subject: [PATCH 19/29] Update the include path for JSONRPC.h --- tests/FunctionalTests/jsonrpc/Module.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/FunctionalTests/jsonrpc/Module.h b/tests/FunctionalTests/jsonrpc/Module.h index 01085b09..8de4584e 100644 --- a/tests/FunctionalTests/jsonrpc/Module.h +++ b/tests/FunctionalTests/jsonrpc/Module.h @@ -25,7 +25,7 @@ #include #include -#include +#include #undef EXTERNAL #define EXTERNAL From 3e7f0d98a689816c8399515843a3755024d1538f Mon Sep 17 00:00:00 2001 From: smanes0213 Date: Wed, 17 Jun 2026 14:15:05 +0530 Subject: [PATCH 20/29] Resolve test failure --- .../jsonrpc/tests/TestStructsJsonRpc.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/FunctionalTests/jsonrpc/tests/TestStructsJsonRpc.cpp b/tests/FunctionalTests/jsonrpc/tests/TestStructsJsonRpc.cpp index def9cd35..a82d33c0 100644 --- a/tests/FunctionalTests/jsonrpc/tests/TestStructsJsonRpc.cpp +++ b/tests/FunctionalTests/jsonrpc/tests/TestStructsJsonRpc.cpp @@ -158,13 +158,13 @@ TEST_F(TestStructsJsonRpc, IsValidRectangle) { TEST_F(TestStructsJsonRpc, Config_Opaque_RoundTrip) { string response; - // SET — the opaque blob is passed as the raw params object + // SET — pass the opaque blob as the value of the "config" field EXPECT_EQ(Core::ERROR_NONE, - CallMethod("config", R"({"level":5,"label":"test"})", response)); + CallMethod("setConfig", R"({"config":{"level":5,"label":"test"}})", response)); response.clear(); // GET — the stored blob is returned verbatim - EXPECT_EQ(Core::ERROR_NONE, CallMethod("config", "{}", response)); + EXPECT_EQ(Core::ERROR_NONE, CallMethod("getConfig", "{}", response)); EXPECT_NE(response.find("level"), string::npos) << "Response: " << response; EXPECT_NE(response.find("test"), string::npos) << "Response: " << response; } @@ -173,13 +173,13 @@ TEST_F(TestStructsJsonRpc, Config_Opaque_RoundTrip) { TEST_F(TestStructsJsonRpc, SlotPoint_IndexedProperty) { string response; - // SET slotPoint@0 — @index makes the slot suffix part of the method name + // SET via setSlotPoint method — slot is a named param, point is the struct EXPECT_EQ(Core::ERROR_NONE, - CallMethod("slotPoint@0", R"({"x":7,"y":13})", response)); + CallMethod("setSlotPoint", R"({"slot":0,"point":{"x":7,"y":13}})", response)); response.clear(); - // GET slotPoint@0 - EXPECT_EQ(Core::ERROR_NONE, CallMethod("slotPoint@0", "{}", response)); + // GET via getSlotPoint@ indexed property + EXPECT_EQ(Core::ERROR_NONE, CallMethod("getSlotPoint@0", "{}", response)); EXPECT_NE(response.find("7"), string::npos) << "Response: " << response; EXPECT_NE(response.find("13"), string::npos) << "Response: " << response; } From 9e06ccd525d840115b19848c843b65348f687faf Mon Sep 17 00:00:00 2001 From: Sankalp Maneshwar Date: Wed, 17 Jun 2026 14:28:34 +0530 Subject: [PATCH 21/29] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- tests/FunctionalTests/jsonrpc/tests/TestStructsJsonRpc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/FunctionalTests/jsonrpc/tests/TestStructsJsonRpc.cpp b/tests/FunctionalTests/jsonrpc/tests/TestStructsJsonRpc.cpp index a82d33c0..a41b0f61 100644 --- a/tests/FunctionalTests/jsonrpc/tests/TestStructsJsonRpc.cpp +++ b/tests/FunctionalTests/jsonrpc/tests/TestStructsJsonRpc.cpp @@ -163,7 +163,7 @@ TEST_F(TestStructsJsonRpc, Config_Opaque_RoundTrip) { CallMethod("setConfig", R"({"config":{"level":5,"label":"test"}})", response)); response.clear(); - // GET — the stored blob is returned verbatim + // GET — verify the stored blob survives the round-trip (key order/formatting may differ) EXPECT_EQ(Core::ERROR_NONE, CallMethod("getConfig", "{}", response)); EXPECT_NE(response.find("level"), string::npos) << "Response: " << response; EXPECT_NE(response.find("test"), string::npos) << "Response: " << response; From 04a7a5f8f4134a5f612b70c960ee87cc4b18e88c Mon Sep 17 00:00:00 2001 From: smanes0213 Date: Thu, 18 Jun 2026 11:16:43 +0530 Subject: [PATCH 22/29] Resolve review comments --- tests/FunctionalTests/CMakeLists.txt | 6 ++++ .../common/interfaces/ITestAsync.h | 2 +- .../ITestJsonUncompliantCollapsed.h | 4 +++ .../interfaces/ITestJsonUncompliantExtended.h | 4 +++ .../common/interfaces/ITestLengthModes.h | 10 ++++-- .../jsonrpc/tests/TestEncodingMacJsonRpc.cpp | 4 +-- .../jsonrpc/tests/TestEnumsJsonRpc.cpp | 25 +++++++-------- .../tests/TestJsonCompliantJsonRpc.cpp | 2 +- .../jsonrpc/tests/TestJsonShapeJsonRpc.cpp | 7 ++-- .../jsonrpc/tests/TestJsonTextCaseJsonRpc.cpp | 2 +- .../jsonrpc/tests/TestJsonTextKeepJsonRpc.cpp | 4 +-- .../TestJsonUncompliantCollapsedJsonRpc.cpp | 2 +- .../TestJsonUncompliantExtendedJsonRpc.cpp | 2 +- .../jsonrpc/tests/TestPrimitivesJsonRpc.cpp | 32 +++++++++---------- .../jsonrpc/tests/TestRestrictionsJsonRpc.cpp | 8 ++--- .../jsonrpc/tests/TestStructsJsonRpc.cpp | 30 +++++++---------- 16 files changed, 77 insertions(+), 67 deletions(-) diff --git a/tests/FunctionalTests/CMakeLists.txt b/tests/FunctionalTests/CMakeLists.txt index d9bffc43..6875ca2a 100644 --- a/tests/FunctionalTests/CMakeLists.txt +++ b/tests/FunctionalTests/CMakeLists.txt @@ -36,6 +36,12 @@ option(TEST_JSON_UNCOMPLIANT_COL "Enable @uncompliant:collapsed format tests" set(CMAKE_MODULE_PATH "${CMAKE_BINARY_DIR}" CACHE BOOL "" FORCE) +# Pre-set generator paths before add_subdirectory(external/thunder) so that +# find_package(ProxyStubGenerator) inside Thunder does not fail on a fresh build. +set(PROXYSTUB_GENERATOR "${CMAKE_SOURCE_DIR}/ProxyStubGenerator/StubGenerator.py" CACHE BOOL "" FORCE) +set(JSON_GENERATOR "${CMAKE_SOURCE_DIR}/JsonGenerator/JsonGenerator.py" CACHE BOOL "" FORCE) +set(CONFIG_GENERATOR_PATH "${CMAKE_SOURCE_DIR}/ConfigGenerator/" CACHE BOOL "" FORCE) + enable_testing() add_subdirectory(external/googletest) diff --git a/tests/FunctionalTests/common/interfaces/ITestAsync.h b/tests/FunctionalTests/common/interfaces/ITestAsync.h index 6f85fe68..c38f1926 100644 --- a/tests/FunctionalTests/common/interfaces/ITestAsync.h +++ b/tests/FunctionalTests/common/interfaces/ITestAsync.h @@ -93,7 +93,7 @@ namespace FunctionalTest { // @param result Receives the computed uint32_t value. // @retval ERROR_INPROGRESS The calculation on this slot has not completed yet. // @retval ERROR_NOT_EXIST The slot was never assigned or has already been consumed. - virtual Core::hresult SlotResult(const uint8_t slot /* @index @restrict:0..6 */, uint32_t& result /* @out */) const = 0; + virtual Core::hresult SlotResult(const uint8_t slot /* @index */, uint32_t& result /* @out */) const = 0; // @brief Returns whether the given slot is currently running a calculation. // @param slot Slot index (0..6) to query. diff --git a/tests/FunctionalTests/common/interfaces/ITestJsonUncompliantCollapsed.h b/tests/FunctionalTests/common/interfaces/ITestJsonUncompliantCollapsed.h index b30e7c66..ad139ae7 100644 --- a/tests/FunctionalTests/common/interfaces/ITestJsonUncompliantCollapsed.h +++ b/tests/FunctionalTests/common/interfaces/ITestJsonUncompliantCollapsed.h @@ -27,6 +27,10 @@ namespace FunctionalTest { // @json 1.0.0 // @uncompliant:collapsed + // + // NOTE: @uncompliant:collapsed is deprecated (Thunder docs/interfaces/tags.md) and must not + // be used in new interfaces. This interface exists solely to pin generator behaviour for + // existing consumers that already use this mode. Do not copy PingCollapsed as a usage example. struct EXTERNAL ITestJsonUncompliantCollapsed : virtual public Core::IUnknown { enum { ID = ID_TEST_JSON_UNCOMPLIANT_COL }; diff --git a/tests/FunctionalTests/common/interfaces/ITestJsonUncompliantExtended.h b/tests/FunctionalTests/common/interfaces/ITestJsonUncompliantExtended.h index 79fefd60..21ba47fa 100644 --- a/tests/FunctionalTests/common/interfaces/ITestJsonUncompliantExtended.h +++ b/tests/FunctionalTests/common/interfaces/ITestJsonUncompliantExtended.h @@ -27,6 +27,10 @@ namespace FunctionalTest { // @json 1.0.0 // @uncompliant:extended + // + // NOTE: @uncompliant:extended is deprecated (Thunder docs/interfaces/tags.md) and must not + // be used in new interfaces. This interface exists solely to pin generator behaviour for + // existing consumers that already use this mode. Do not copy PingExtended as a usage example. struct EXTERNAL ITestJsonUncompliantExtended : virtual public Core::IUnknown { enum { ID = ID_TEST_JSON_UNCOMPLIANT_EXT }; diff --git a/tests/FunctionalTests/common/interfaces/ITestLengthModes.h b/tests/FunctionalTests/common/interfaces/ITestLengthModes.h index 1ec3b76d..b7d5b456 100644 --- a/tests/FunctionalTests/common/interfaces/ITestLengthModes.h +++ b/tests/FunctionalTests/common/interfaces/ITestLengthModes.h @@ -41,8 +41,14 @@ namespace FunctionalTest { uint8_t output[] /* @out @length:1 @maxlength:1 */) const = 0; // @brief Fills an output buffer and returns the payload length directly. - // Intended to cover the length:return annotation in a signature - // where return type carries the produced byte count. + // @brief Returns a fixed 4-byte payload using @length:return to convey the written byte count. + // Intended to cover the @length:return annotation in a signature + // where the return type carries the produced byte count. + // + // NOTE: returning uint16_t instead of Core::hresult is a deliberate trade-off for + // test coverage of the @length:return code path, not an oversight. The downside is + // that callers lose the ability to detect transport-level failures: there is no + // hresult channel through which such errors can be signalled. // @param output Receives payload bytes. // @param maxSize Maximum writable bytes in output. // @retval Payload byte count written to output. diff --git a/tests/FunctionalTests/jsonrpc/tests/TestEncodingMacJsonRpc.cpp b/tests/FunctionalTests/jsonrpc/tests/TestEncodingMacJsonRpc.cpp index dce35f95..3c96d2c0 100644 --- a/tests/FunctionalTests/jsonrpc/tests/TestEncodingMacJsonRpc.cpp +++ b/tests/FunctionalTests/jsonrpc/tests/TestEncodingMacJsonRpc.cpp @@ -34,7 +34,7 @@ TEST_F(TestEncodingMacJsonRpc, SetGetMacAddress) { response.clear(); EXPECT_EQ(Core::ERROR_NONE, CallMethod("getMacAddress", "{}", response)); // verify the stored MAC is returned correctly - EXPECT_NE(response.find("01:23:45:67:89:ab"), string::npos); + EXPECT_EQ(response, "\"01:23:45:67:89:ab\"") << "Response: " << response; } TEST_F(TestEncodingMacJsonRpc, EchoMacAddress) { @@ -42,5 +42,5 @@ TEST_F(TestEncodingMacJsonRpc, EchoMacAddress) { EXPECT_EQ(Core::ERROR_NONE, CallMethod("echoMacAddress", R"({"input":"de:ad:be:ef:00:01"})", response)); // verify the echoed MAC matches the input - EXPECT_NE(response.find("de:ad:be:ef:00:01"), string::npos); + EXPECT_EQ(response, "\"de:ad:be:ef:00:01\"") << "Response: " << response; } diff --git a/tests/FunctionalTests/jsonrpc/tests/TestEnumsJsonRpc.cpp b/tests/FunctionalTests/jsonrpc/tests/TestEnumsJsonRpc.cpp index d5498a36..8ce09011 100644 --- a/tests/FunctionalTests/jsonrpc/tests/TestEnumsJsonRpc.cpp +++ b/tests/FunctionalTests/jsonrpc/tests/TestEnumsJsonRpc.cpp @@ -39,17 +39,17 @@ TEST_F(TestEnumsJsonRpc, DISABLED_SetGetColor) { TEST_F(TestEnumsJsonRpc, ToggleColor) { string response; EXPECT_EQ(Core::ERROR_NONE, CallMethod("toggleColor", R"({"color":"RED"})", response)); - EXPECT_NE(response.find("GREEN"), string::npos) << "Response: " << response; + EXPECT_EQ(response, "\"GREEN\"") << "Response: " << response; } TEST_F(TestEnumsJsonRpc, CompareColors) { string response; EXPECT_EQ(Core::ERROR_NONE, CallMethod("compareColors", R"({"color1":"RED","color2":"RED"})", response)); - EXPECT_NE(response.find("true"), string::npos) << "Response: " << response; + EXPECT_EQ(response, "true") << "Response: " << response; response.clear(); EXPECT_EQ(Core::ERROR_NONE, CallMethod("compareColors", R"({"color1":"RED","color2":"BLUE"})", response)); - EXPECT_NE(response.find("false"), string::npos) << "Response: " << response; + EXPECT_EQ(response, "false") << "Response: " << response; } TEST_F(TestEnumsJsonRpc, SetGetState) { @@ -58,13 +58,13 @@ TEST_F(TestEnumsJsonRpc, SetGetState) { response.clear(); EXPECT_EQ(Core::ERROR_NONE, CallMethod("getState", "{}", response)); - EXPECT_NE(response.find("RUNNING"), string::npos) << "Response: " << response; + EXPECT_EQ(response, "\"RUNNING\"") << "Response: " << response; } TEST_F(TestEnumsJsonRpc, NextState) { string response; EXPECT_EQ(Core::ERROR_NONE, CallMethod("nextState", R"({"state":"IDLE"})", response)); - EXPECT_NE(response.find("RUNNING"), string::npos) << "Response: " << response; + EXPECT_EQ(response, "\"RUNNING\"") << "Response: " << response; } TEST_F(TestEnumsJsonRpc, SetGetPriority) { @@ -73,7 +73,7 @@ TEST_F(TestEnumsJsonRpc, SetGetPriority) { response.clear(); EXPECT_EQ(Core::ERROR_NONE, CallMethod("getPriority", "{}", response)); - EXPECT_NE(response.find("HIGH"), string::npos) << "Response: " << response; + EXPECT_EQ(response, "\"HIGH\"") << "Response: " << response; } TEST_F(TestEnumsJsonRpc, SetGetCapabilities) { @@ -83,7 +83,9 @@ TEST_F(TestEnumsJsonRpc, SetGetCapabilities) { response.clear(); EXPECT_EQ(Core::ERROR_NONE, CallMethod("getCapabilities", "{}", response)); + // bitmask serialised as array; both flags must be present (order unspecified) EXPECT_NE(response.find("CAP_AUDIO"), string::npos) << "Response: " << response; + EXPECT_NE(response.find("CAP_VIDEO"), string::npos) << "Response: " << response; } TEST_F(TestEnumsJsonRpc, CurrentState_PropertyReadOnly) { @@ -103,19 +105,14 @@ TEST_F(TestEnumsJsonRpc, ComputeState) { string response; EXPECT_EQ(Core::ERROR_NONE, CallMethod("computeState", R"({"current":"RUNNING","desired":"STOPPED"})", response)); - // Should return derived state - expecting a valid State enum value - bool hasValidState = (response.find("IDLE") != string::npos || - response.find("RUNNING") != string::npos || - response.find("PAUSED") != string::npos || - response.find("STOPPED") != string::npos || - response.find("ERROR") != string::npos); - EXPECT_TRUE(hasValidState) << "Response should contain a valid derived State enum value: " << response; + // RUNNING != STOPPED → impl returns PAUSED + EXPECT_EQ(response, "\"PAUSED\"") << "Response: " << response; } TEST_F(TestEnumsJsonRpc, IsValidState) { string response; EXPECT_EQ(Core::ERROR_NONE, CallMethod("isValidState", R"({"state":"RUNNING"})", response)); - EXPECT_NE(response.find("true"), string::npos) << "Response: " << response; + EXPECT_EQ(response, "true") << "Response: " << response; } diff --git a/tests/FunctionalTests/jsonrpc/tests/TestJsonCompliantJsonRpc.cpp b/tests/FunctionalTests/jsonrpc/tests/TestJsonCompliantJsonRpc.cpp index 37b4d164..740de0c0 100644 --- a/tests/FunctionalTests/jsonrpc/tests/TestJsonCompliantJsonRpc.cpp +++ b/tests/FunctionalTests/jsonrpc/tests/TestJsonCompliantJsonRpc.cpp @@ -29,5 +29,5 @@ TEST_F(TestJsonCompliantJsonRpc, Ping) { string response; EXPECT_EQ(Core::ERROR_NONE, CallMethod("ping", R"({"payload":"abc"})", response)); // impl echoes payload into reply; verify the value survives the compliant envelope - EXPECT_NE(response.find("abc"), string::npos); + EXPECT_EQ(response, "\"abc\"") << "Response: " << response; } diff --git a/tests/FunctionalTests/jsonrpc/tests/TestJsonShapeJsonRpc.cpp b/tests/FunctionalTests/jsonrpc/tests/TestJsonShapeJsonRpc.cpp index 3546a425..94b8292f 100644 --- a/tests/FunctionalTests/jsonrpc/tests/TestJsonShapeJsonRpc.cpp +++ b/tests/FunctionalTests/jsonrpc/tests/TestJsonShapeJsonRpc.cpp @@ -29,7 +29,7 @@ TEST_F(TestJsonShapeJsonRpc, GetWrappedCounter) { string response; EXPECT_EQ(Core::ERROR_NONE, CallMethod("getWrappedCounter", "{}", response)); // impl counter is initialised to 42; @wrapped encloses it in an object envelope - EXPECT_NE(response.find("42"), string::npos); + EXPECT_EQ(response, "{\"counter\":42}") << "Response: " << response; } TEST_F(TestJsonShapeJsonRpc, EchoExtractedList) { @@ -37,7 +37,7 @@ TEST_F(TestJsonShapeJsonRpc, EchoExtractedList) { EXPECT_EQ(Core::ERROR_NONE, CallMethod("echoExtractedList", R"({"input":[7]})", response)); // verify the echoed list element survives the @extract round-trip - EXPECT_NE(response.find("7"), string::npos); + EXPECT_EQ(response, "[7]") << "Response: " << response; } TEST_F(TestJsonShapeJsonRpc, EchoStruct) { @@ -45,6 +45,5 @@ TEST_F(TestJsonShapeJsonRpc, EchoStruct) { EXPECT_EQ(Core::ERROR_NONE, CallMethod("echoStruct", R"({"in":{"width":1920,"height":1080}})", response)); // verify both struct fields survive the standard (non-extracted) round-trip - EXPECT_NE(response.find("1920"), string::npos); - EXPECT_NE(response.find("1080"), string::npos); + EXPECT_EQ(response, "{\"width\":1920,\"height\":1080}") << "Response: " << response; } diff --git a/tests/FunctionalTests/jsonrpc/tests/TestJsonTextCaseJsonRpc.cpp b/tests/FunctionalTests/jsonrpc/tests/TestJsonTextCaseJsonRpc.cpp index cae3415c..62f832d5 100644 --- a/tests/FunctionalTests/jsonrpc/tests/TestJsonTextCaseJsonRpc.cpp +++ b/tests/FunctionalTests/jsonrpc/tests/TestJsonTextCaseJsonRpc.cpp @@ -30,5 +30,5 @@ TEST_F(TestJsonTextCaseJsonRpc, EchoCaseConvention) { EXPECT_EQ(Core::ERROR_NONE, CallMethod("echocaseconvention", R"({"sourcevalue":123})", response)); // @text:legacy lowercases method/param names; verify the echo-back value is correct - EXPECT_NE(response.find("123"), string::npos); + EXPECT_EQ(response, "123") << "Response: " << response; } diff --git a/tests/FunctionalTests/jsonrpc/tests/TestJsonTextKeepJsonRpc.cpp b/tests/FunctionalTests/jsonrpc/tests/TestJsonTextKeepJsonRpc.cpp index 376ea04c..707ada17 100644 --- a/tests/FunctionalTests/jsonrpc/tests/TestJsonTextKeepJsonRpc.cpp +++ b/tests/FunctionalTests/jsonrpc/tests/TestJsonTextKeepJsonRpc.cpp @@ -30,7 +30,7 @@ TEST_F(TestJsonTextKeepJsonRpc, EchoMixedCaseName) { EXPECT_EQ(Core::ERROR_NONE, CallMethod("EchoMixedCaseName", R"({"InputValue":77})", response)); // @text:keep preserves exact C++ casing; verify the echo-back value is correct - EXPECT_NE(response.find("77"), string::npos); + EXPECT_EQ(response, "77") << "Response: " << response; } TEST_F(TestJsonTextKeepJsonRpc, BuildVersionProperty) { @@ -38,5 +38,5 @@ TEST_F(TestJsonTextKeepJsonRpc, BuildVersionProperty) { EXPECT_EQ(Core::ERROR_NONE, CallMethod("BuildVersion", "{}", response)); // impl returns hardcoded version 42; search for the bare number (won't appear in the JSON-RPC envelope) - EXPECT_NE(response.find("42"), string::npos); + EXPECT_EQ(response, "42") << "Response: " << response; } diff --git a/tests/FunctionalTests/jsonrpc/tests/TestJsonUncompliantCollapsedJsonRpc.cpp b/tests/FunctionalTests/jsonrpc/tests/TestJsonUncompliantCollapsedJsonRpc.cpp index f4693509..7bd4aac7 100644 --- a/tests/FunctionalTests/jsonrpc/tests/TestJsonUncompliantCollapsedJsonRpc.cpp +++ b/tests/FunctionalTests/jsonrpc/tests/TestJsonUncompliantCollapsedJsonRpc.cpp @@ -30,5 +30,5 @@ TEST_F(TestJsonUncompliantCollapsedJsonRpc, PingCollapsed) { EXPECT_EQ(Core::ERROR_NONE, CallMethod("pingCollapsed", R"("abc")", response)); // impl echoes payload; collapsed mode passes and returns raw JSON string - EXPECT_NE(response.find("abc"), string::npos); + EXPECT_EQ(response, "\"abc\"") << "Response: " << response; } diff --git a/tests/FunctionalTests/jsonrpc/tests/TestJsonUncompliantExtendedJsonRpc.cpp b/tests/FunctionalTests/jsonrpc/tests/TestJsonUncompliantExtendedJsonRpc.cpp index 4aa2447c..f64bc69a 100644 --- a/tests/FunctionalTests/jsonrpc/tests/TestJsonUncompliantExtendedJsonRpc.cpp +++ b/tests/FunctionalTests/jsonrpc/tests/TestJsonUncompliantExtendedJsonRpc.cpp @@ -30,5 +30,5 @@ TEST_F(TestJsonUncompliantExtendedJsonRpc, PingExtended) { EXPECT_EQ(Core::ERROR_NONE, CallMethod("pingExtended", R"({"payload":"abc"})", response)); // impl echoes payload into reply; verify the value survives the extended uncompliant envelope - EXPECT_NE(response.find("abc"), string::npos); + EXPECT_EQ(response, "\"abc\"") << "Response: " << response; } diff --git a/tests/FunctionalTests/jsonrpc/tests/TestPrimitivesJsonRpc.cpp b/tests/FunctionalTests/jsonrpc/tests/TestPrimitivesJsonRpc.cpp index a93cbf90..9b0e288d 100644 --- a/tests/FunctionalTests/jsonrpc/tests/TestPrimitivesJsonRpc.cpp +++ b/tests/FunctionalTests/jsonrpc/tests/TestPrimitivesJsonRpc.cpp @@ -32,7 +32,7 @@ class TestPrimitivesJsonRpc : public JsonRpcTesting::JsonRpcTestHarness {}; TEST_F(TestPrimitivesJsonRpc, EchoInt8_Positive) { string response; EXPECT_EQ(Core::ERROR_NONE, CallMethod("echoInt8", R"({"input":42})", response)); - EXPECT_NE(response.find("42"), string::npos) << "Response: " << response; + EXPECT_EQ(response, "42") << "Response: " << response; } // DISABLED: JsonGenerator DecSInt validation bug - boundary values fail with overflow errors @@ -50,7 +50,7 @@ TEST_F(TestPrimitivesJsonRpc, DISABLED_EchoInt8_Boundaries) { TEST_F(TestPrimitivesJsonRpc, EchoInt16_Positive) { string response; EXPECT_EQ(Core::ERROR_NONE, CallMethod("echoInt16", R"({"input":1000})", response)); - EXPECT_NE(response.find("1000"), string::npos) << "Response: " << response; + EXPECT_EQ(response, "1000") << "Response: " << response; } // DISABLED: JsonGenerator DecSInt validation bug - boundary values fail with overflow errors @@ -68,7 +68,7 @@ TEST_F(TestPrimitivesJsonRpc, DISABLED_EchoInt16_Boundaries) { TEST_F(TestPrimitivesJsonRpc, EchoInt32_Positive) { string response; EXPECT_EQ(Core::ERROR_NONE, CallMethod("echoInt32", R"({"input":100000})", response)); - EXPECT_NE(response.find("100000"), string::npos) << "Response: " << response; + EXPECT_EQ(response, "100000") << "Response: " << response; } // DISABLED: JsonGenerator DecSInt validation bug - boundary values fail with overflow errors @@ -86,7 +86,7 @@ TEST_F(TestPrimitivesJsonRpc, DISABLED_EchoInt32_Boundaries) { TEST_F(TestPrimitivesJsonRpc, EchoInt64_Positive) { string response; EXPECT_EQ(Core::ERROR_NONE, CallMethod("echoInt64", R"({"input":9223372036854775})", response)); - EXPECT_NE(response.find("9223372036854775"), string::npos) << "Response: " << response; + EXPECT_EQ(response, "9223372036854775") << "Response: " << response; } // DISABLED: JsonGenerator DecSInt validation bug - boundary values fail with overflow errors @@ -107,44 +107,44 @@ TEST_F(TestPrimitivesJsonRpc, EchoUInt8_Boundaries) { string response; EXPECT_EQ(Core::ERROR_NONE, CallMethod("echoUInt8", R"({"input":0})", response)); - EXPECT_NE(response.find("0"), string::npos) << "Response: " << response; + EXPECT_EQ(response, "0") << "Response: " << response; response.clear(); EXPECT_EQ(Core::ERROR_NONE, CallMethod("echoUInt8", R"({"input":255})", response)); - EXPECT_NE(response.find("255"), string::npos) << "Response: " << response; + EXPECT_EQ(response, "255") << "Response: " << response; } TEST_F(TestPrimitivesJsonRpc, EchoUInt16_Boundaries) { string response; EXPECT_EQ(Core::ERROR_NONE, CallMethod("echoUInt16", R"({"input":0})", response)); - EXPECT_NE(response.find("0"), string::npos) << "Response: " << response; + EXPECT_EQ(response, "0") << "Response: " << response; response.clear(); EXPECT_EQ(Core::ERROR_NONE, CallMethod("echoUInt16", R"({"input":65535})", response)); - EXPECT_NE(response.find("65535"), string::npos) << "Response: " << response; + EXPECT_EQ(response, "65535") << "Response: " << response; } TEST_F(TestPrimitivesJsonRpc, EchoUInt32_Boundaries) { string response; EXPECT_EQ(Core::ERROR_NONE, CallMethod("echoUInt32", R"({"input":0})", response)); - EXPECT_NE(response.find("0"), string::npos) << "Response: " << response; + EXPECT_EQ(response, "0") << "Response: " << response; response.clear(); EXPECT_EQ(Core::ERROR_NONE, CallMethod("echoUInt32", R"({"input":4294967295})", response)); - EXPECT_NE(response.find("4294967295"), string::npos) << "Response: " << response; + EXPECT_EQ(response, "4294967295") << "Response: " << response; } TEST_F(TestPrimitivesJsonRpc, EchoUInt64_Boundaries) { string response; EXPECT_EQ(Core::ERROR_NONE, CallMethod("echoUInt64", R"({"input":0})", response)); - EXPECT_NE(response.find("0"), string::npos) << "Response: " << response; + EXPECT_EQ(response, "0") << "Response: " << response; response.clear(); EXPECT_EQ(Core::ERROR_NONE, CallMethod("echoUInt64", R"({"input":18446744073709551615})", response)); - EXPECT_NE(response.find("18446744073709551615"), string::npos) << "Response: " << response; + EXPECT_EQ(response, "18446744073709551615") << "Response: " << response; } // ===== Floating point ===== @@ -167,11 +167,11 @@ TEST_F(TestPrimitivesJsonRpc, EchoBool) { string response; EXPECT_EQ(Core::ERROR_NONE, CallMethod("echoBool", R"({"input":true})", response)); - EXPECT_NE(response.find("true"), string::npos) << "Response: " << response; + EXPECT_EQ(response, "true") << "Response: " << response; response.clear(); EXPECT_EQ(Core::ERROR_NONE, CallMethod("echoBool", R"({"input":false})", response)); - EXPECT_NE(response.find("false"), string::npos) << "Response: " << response; + EXPECT_EQ(response, "false") << "Response: " << response; } // ===== String ===== @@ -179,13 +179,13 @@ TEST_F(TestPrimitivesJsonRpc, EchoBool) { TEST_F(TestPrimitivesJsonRpc, EchoString_UTF8) { string response; EXPECT_EQ(Core::ERROR_NONE, CallMethod("echoString", R"({"input":"Hello JSON-RPC!"})", response)); - EXPECT_NE(response.find("Hello JSON-RPC!"), string::npos) << "Response: " << response; + EXPECT_EQ(response, "\"Hello JSON-RPC!\"") << "Response: " << response; } TEST_F(TestPrimitivesJsonRpc, EchoString_Empty) { string response; EXPECT_EQ(Core::ERROR_NONE, CallMethod("echoString", R"({"input":""})", response)); - EXPECT_NE(response.find("\""), string::npos) << "Response: " << response; + EXPECT_EQ(response, "\"\"") << "Response: " << response; } // ===== Special types ===== diff --git a/tests/FunctionalTests/jsonrpc/tests/TestRestrictionsJsonRpc.cpp b/tests/FunctionalTests/jsonrpc/tests/TestRestrictionsJsonRpc.cpp index 5f1ea3c9..6be1f9d1 100644 --- a/tests/FunctionalTests/jsonrpc/tests/TestRestrictionsJsonRpc.cpp +++ b/tests/FunctionalTests/jsonrpc/tests/TestRestrictionsJsonRpc.cpp @@ -76,18 +76,18 @@ TEST_F(TestRestrictionsJsonRpc, SetName_TooLongRejected) { TEST_F(TestRestrictionsJsonRpc, ClampedAdd_ValidInputs) { string response; EXPECT_EQ(Core::ERROR_NONE, CallMethod("clampedAdd", R"({"a":10,"b":20})", response)); - EXPECT_NE(response.find("30"), string::npos) << "Response: " << response; + EXPECT_EQ(response, "30") << "Response: " << response; } TEST_F(TestRestrictionsJsonRpc, IsValidPercentage) { string response; EXPECT_EQ(Core::ERROR_NONE, CallMethod("isValidPercentage", R"({"value":50})", response)); - EXPECT_NE(response.find("true"), string::npos) << "Response: " << response; + EXPECT_EQ(response, "true") << "Response: " << response; response.clear(); EXPECT_EQ(Core::ERROR_NONE, CallMethod("isValidPercentage", R"({"value":150})", response)); - EXPECT_NE(response.find("false"), string::npos) << "Response: " << response; + EXPECT_EQ(response, "false") << "Response: " << response; } TEST_F(TestRestrictionsJsonRpc, IsValidRatio) { @@ -95,6 +95,6 @@ TEST_F(TestRestrictionsJsonRpc, IsValidRatio) { // Parameter name is "ratio" not "value" EXPECT_EQ(Core::ERROR_NONE, CallMethod("isValidRatio", R"({"ratio":0.5})", response)); - EXPECT_NE(response.find("true"), string::npos) << "Response: " << response; + EXPECT_EQ(response, "true") << "Response: " << response; } diff --git a/tests/FunctionalTests/jsonrpc/tests/TestStructsJsonRpc.cpp b/tests/FunctionalTests/jsonrpc/tests/TestStructsJsonRpc.cpp index a41b0f61..8dc163bb 100644 --- a/tests/FunctionalTests/jsonrpc/tests/TestStructsJsonRpc.cpp +++ b/tests/FunctionalTests/jsonrpc/tests/TestStructsJsonRpc.cpp @@ -35,8 +35,7 @@ TEST_F(TestStructsJsonRpc, SetGetPoint_RoundTrip) { // Get point back response.clear(); EXPECT_EQ(Core::ERROR_NONE, CallMethod("getPoint", "{}", response)); - EXPECT_NE(response.find("100"), string::npos) << "Response: " << response; - EXPECT_NE(response.find("200"), string::npos) << "Response: " << response; + EXPECT_EQ(response, "{\"x\":100,\"y\":200}") << "Response: " << response; } TEST_F(TestStructsJsonRpc, SetGetRectangle_RoundTrip) { @@ -48,8 +47,7 @@ TEST_F(TestStructsJsonRpc, SetGetRectangle_RoundTrip) { response.clear(); EXPECT_EQ(Core::ERROR_NONE, CallMethod("getRectangle", "{}", response)); - EXPECT_NE(response.find("10"), string::npos) << "Response: " << response; - EXPECT_NE(response.find("20"), string::npos) << "Response: " << response; + EXPECT_EQ(response, "{\"topLeft\":{\"x\":10,\"y\":20},\"bottomRight\":{\"x\":310,\"y\":420}}") << "Response: " << response; } // DISABLED: setColor/getColor methods fail with error 30 (not found) despite being registered in JTestStructs.h. @@ -74,8 +72,7 @@ TEST_F(TestStructsJsonRpc, MovePoint) { EXPECT_EQ(Core::ERROR_NONE, CallMethod("movePoint", R"({"point":{"x":100,"y":200},"dx":50,"dy":-30})", response)); - EXPECT_NE(response.find("150"), string::npos) << "Response: " << response; - EXPECT_NE(response.find("170"), string::npos) << "Response: " << response; + EXPECT_EQ(response, "{\"x\":150,\"y\":170}") << "Response: " << response; } TEST_F(TestStructsJsonRpc, DistanceBetweenPoints_Pythagorean) { @@ -101,8 +98,7 @@ TEST_F(TestStructsJsonRpc, EchoPoint) { // echoPoint expects "point" parameter EXPECT_EQ(Core::ERROR_NONE, CallMethod("echoPoint", R"({"point":{"x":42,"y":84}})", response)); - EXPECT_NE(response.find("42"), string::npos) << "Response: " << response; - EXPECT_NE(response.find("84"), string::npos) << "Response: " << response; + EXPECT_EQ(response, "{\"x\":42,\"y\":84}") << "Response: " << response; } TEST_F(TestStructsJsonRpc, IsValidPoint) { @@ -110,7 +106,7 @@ TEST_F(TestStructsJsonRpc, IsValidPoint) { EXPECT_EQ(Core::ERROR_NONE, CallMethod("isValidPoint", R"({"point":{"x":100,"y":200}})", response)); - EXPECT_NE(response.find("true"), string::npos) << "Response: " << response; + EXPECT_EQ(response, "true") << "Response: " << response; } TEST_F(TestStructsJsonRpc, CalculateRectangleArea) { @@ -118,7 +114,7 @@ TEST_F(TestStructsJsonRpc, CalculateRectangleArea) { EXPECT_EQ(Core::ERROR_NONE, CallMethod("calculateRectangleArea", R"({"rect":{"topLeft":{"x":0,"y":0},"bottomRight":{"x":10,"y":20}}})", response)); - EXPECT_NE(response.find("200"), string::npos) << "Response: " << response; // 10 * 20 = 200 + EXPECT_EQ(response, "200") << "Response: " << response; // 10 * 20 = 200 } TEST_F(TestStructsJsonRpc, GetRectangleCenter) { @@ -126,7 +122,7 @@ TEST_F(TestStructsJsonRpc, GetRectangleCenter) { EXPECT_EQ(Core::ERROR_NONE, CallMethod("getRectangleCenter", R"({"rect":{"topLeft":{"x":0,"y":0},"bottomRight":{"x":10,"y":20}}})", response)); - EXPECT_NE(response.find("5"), string::npos) << "Response: " << response; // center x=5 + EXPECT_EQ(response, "{\"x\":5,\"y\":10}") << "Response: " << response; } TEST_F(TestStructsJsonRpc, RectanglesOverlap) { @@ -135,7 +131,7 @@ TEST_F(TestStructsJsonRpc, RectanglesOverlap) { EXPECT_EQ(Core::ERROR_NONE, CallMethod("rectanglesOverlap", R"({"r1":{"topLeft":{"x":0,"y":0},"bottomRight":{"x":10,"y":10}}, "r2":{"topLeft":{"x":5,"y":5},"bottomRight":{"x":15,"y":15}}})", response)); - EXPECT_NE(response.find("true"), string::npos) << "Response: " << response; + EXPECT_EQ(response, "true") << "Response: " << response; } TEST_F(TestStructsJsonRpc, ScaleRectangle) { @@ -143,7 +139,7 @@ TEST_F(TestStructsJsonRpc, ScaleRectangle) { EXPECT_EQ(Core::ERROR_NONE, CallMethod("scaleRectangle", R"({"rect":{"topLeft":{"x":0,"y":0},"bottomRight":{"x":10,"y":10}},"factor":2.0})", response)); - EXPECT_NE(response.find("20"), string::npos) << "Response: " << response; // scaled to 20x20 + EXPECT_EQ(response, "{\"topLeft\":{\"x\":0,\"y\":0},\"bottomRight\":{\"x\":20,\"y\":20}}") << "Response: " << response; } TEST_F(TestStructsJsonRpc, IsValidRectangle) { @@ -151,7 +147,7 @@ TEST_F(TestStructsJsonRpc, IsValidRectangle) { EXPECT_EQ(Core::ERROR_NONE, CallMethod("isValidRectangle", R"({"rect":{"topLeft":{"x":0,"y":0},"bottomRight":{"x":10,"y":10}}})", response)); - EXPECT_NE(response.find("true"), string::npos) << "Response: " << response; + EXPECT_EQ(response, "true") << "Response: " << response; } // ===== @opaque — config property passes its JSON blob through without deserialisation ===== @@ -165,8 +161,7 @@ TEST_F(TestStructsJsonRpc, Config_Opaque_RoundTrip) { response.clear(); // GET — verify the stored blob survives the round-trip (key order/formatting may differ) EXPECT_EQ(Core::ERROR_NONE, CallMethod("getConfig", "{}", response)); - EXPECT_NE(response.find("level"), string::npos) << "Response: " << response; - EXPECT_NE(response.find("test"), string::npos) << "Response: " << response; + EXPECT_EQ(response, "{\"level\":5,\"label\":\"test\"}") << "Response: " << response; } // ===== @index — slotPoint@ addresses each slot independently ===== @@ -180,7 +175,6 @@ TEST_F(TestStructsJsonRpc, SlotPoint_IndexedProperty) { response.clear(); // GET via getSlotPoint@ indexed property EXPECT_EQ(Core::ERROR_NONE, CallMethod("getSlotPoint@0", "{}", response)); - EXPECT_NE(response.find("7"), string::npos) << "Response: " << response; - EXPECT_NE(response.find("13"), string::npos) << "Response: " << response; + EXPECT_EQ(response, "{\"x\":7,\"y\":13}") << "Response: " << response; } From 6831e12f25f326fd1edaf02fb35035c82ab33662 Mon Sep 17 00:00:00 2001 From: smanes0213 Date: Thu, 18 Jun 2026 11:17:20 +0530 Subject: [PATCH 23/29] Resolve review comments --- tests/FunctionalTests/common/interfaces/ITestAsync.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/FunctionalTests/common/interfaces/ITestAsync.h b/tests/FunctionalTests/common/interfaces/ITestAsync.h index c38f1926..6f85fe68 100644 --- a/tests/FunctionalTests/common/interfaces/ITestAsync.h +++ b/tests/FunctionalTests/common/interfaces/ITestAsync.h @@ -93,7 +93,7 @@ namespace FunctionalTest { // @param result Receives the computed uint32_t value. // @retval ERROR_INPROGRESS The calculation on this slot has not completed yet. // @retval ERROR_NOT_EXIST The slot was never assigned or has already been consumed. - virtual Core::hresult SlotResult(const uint8_t slot /* @index */, uint32_t& result /* @out */) const = 0; + virtual Core::hresult SlotResult(const uint8_t slot /* @index @restrict:0..6 */, uint32_t& result /* @out */) const = 0; // @brief Returns whether the given slot is currently running a calculation. // @param slot Slot index (0..6) to query. From 21fd51f84f14cb48b48db7fcb7d66a4c1097f563 Mon Sep 17 00:00:00 2001 From: smanes0213 Date: Thu, 18 Jun 2026 11:36:43 +0530 Subject: [PATCH 24/29] Resolve workflow error --- tests/FunctionalTests/common/interfaces/ITestLengthModes.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/FunctionalTests/common/interfaces/ITestLengthModes.h b/tests/FunctionalTests/common/interfaces/ITestLengthModes.h index b7d5b456..5751aa6e 100644 --- a/tests/FunctionalTests/common/interfaces/ITestLengthModes.h +++ b/tests/FunctionalTests/common/interfaces/ITestLengthModes.h @@ -41,12 +41,12 @@ namespace FunctionalTest { uint8_t output[] /* @out @length:1 @maxlength:1 */) const = 0; // @brief Fills an output buffer and returns the payload length directly. - // @brief Returns a fixed 4-byte payload using @length:return to convey the written byte count. - // Intended to cover the @length:return annotation in a signature + // @brief Returns a fixed 4-byte payload using the length:return annotation to convey the written byte count. + // Intended to cover the length:return annotation in a signature // where the return type carries the produced byte count. // // NOTE: returning uint16_t instead of Core::hresult is a deliberate trade-off for - // test coverage of the @length:return code path, not an oversight. The downside is + // test coverage of the length:return code path, not an oversight. The downside is // that callers lose the ability to detect transport-level failures: there is no // hresult channel through which such errors can be signalled. // @param output Receives payload bytes. From 90ff5ac309b545c93f3bd26016df6f3056454587 Mon Sep 17 00:00:00 2001 From: smanes0213 Date: Thu, 18 Jun 2026 11:54:35 +0530 Subject: [PATCH 25/29] Resolve workflow error --- tests/FunctionalTests/CMakeLists.txt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/FunctionalTests/CMakeLists.txt b/tests/FunctionalTests/CMakeLists.txt index 6875ca2a..d9bffc43 100644 --- a/tests/FunctionalTests/CMakeLists.txt +++ b/tests/FunctionalTests/CMakeLists.txt @@ -36,12 +36,6 @@ option(TEST_JSON_UNCOMPLIANT_COL "Enable @uncompliant:collapsed format tests" set(CMAKE_MODULE_PATH "${CMAKE_BINARY_DIR}" CACHE BOOL "" FORCE) -# Pre-set generator paths before add_subdirectory(external/thunder) so that -# find_package(ProxyStubGenerator) inside Thunder does not fail on a fresh build. -set(PROXYSTUB_GENERATOR "${CMAKE_SOURCE_DIR}/ProxyStubGenerator/StubGenerator.py" CACHE BOOL "" FORCE) -set(JSON_GENERATOR "${CMAKE_SOURCE_DIR}/JsonGenerator/JsonGenerator.py" CACHE BOOL "" FORCE) -set(CONFIG_GENERATOR_PATH "${CMAKE_SOURCE_DIR}/ConfigGenerator/" CACHE BOOL "" FORCE) - enable_testing() add_subdirectory(external/googletest) From 22e3d237a4e2a67d3e6da0e475ae4bf9dcf5448d Mon Sep 17 00:00:00 2001 From: smanes0213 Date: Thu, 18 Jun 2026 11:57:32 +0530 Subject: [PATCH 26/29] Resolve copilot review comments --- .../common/interfaces/ITestLengthModes.h | 5 ++- .../jsonrpc/tests/TestAsyncJsonRpc.cpp | 35 +++++++++++++------ 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/tests/FunctionalTests/common/interfaces/ITestLengthModes.h b/tests/FunctionalTests/common/interfaces/ITestLengthModes.h index 5751aa6e..6dbed370 100644 --- a/tests/FunctionalTests/common/interfaces/ITestLengthModes.h +++ b/tests/FunctionalTests/common/interfaces/ITestLengthModes.h @@ -40,10 +40,9 @@ namespace FunctionalTest { const uint8_t input /* @in */, uint8_t output[] /* @out @length:1 @maxlength:1 */) const = 0; - // @brief Fills an output buffer and returns the payload length directly. // @brief Returns a fixed 4-byte payload using the length:return annotation to convey the written byte count. - // Intended to cover the length:return annotation in a signature - // where the return type carries the produced byte count. + // Intended to cover the length:return annotation in a signature where the return type + // carries the produced byte count. // // NOTE: returning uint16_t instead of Core::hresult is a deliberate trade-off for // test coverage of the length:return code path, not an oversight. The downside is diff --git a/tests/FunctionalTests/jsonrpc/tests/TestAsyncJsonRpc.cpp b/tests/FunctionalTests/jsonrpc/tests/TestAsyncJsonRpc.cpp index 869d8951..8f5efde6 100644 --- a/tests/FunctionalTests/jsonrpc/tests/TestAsyncJsonRpc.cpp +++ b/tests/FunctionalTests/jsonrpc/tests/TestAsyncJsonRpc.cpp @@ -52,6 +52,24 @@ static uint8_t ParseSlot(const string& response) return static_cast(std::stoi(response.substr(pos + 7))); } +// Poll slotResult@ until it returns Core::ERROR_NONE or the deadline elapses. +// Retries every 20 ms so the test is not sensitive to a fixed delay. +static Core::hresult PollSlotResult(JsonRpcTesting::JsonRpcTestHarness& harness, + uint8_t slot, string& response, + std::chrono::milliseconds timeout = std::chrono::milliseconds(2000)) +{ + const auto deadline = std::chrono::steady_clock::now() + timeout; + Core::hresult result = Core::ERROR_GENERAL; + do { + result = harness.CallMethod("slotResult@" + std::to_string(slot), "{}", response); + if (result == Core::ERROR_NONE) { + break; + } + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + } while (std::chrono::steady_clock::now() < deadline); + return result; +} + // @async — calculate() returns a slot index synchronously in the JSON-RPC response; // the ICallback is replaced by the framework's event-delivery mechanism. TEST_F(TestAsyncJsonRpc, Calculate_ReturnsSlot) @@ -76,10 +94,10 @@ TEST_F(TestAsyncJsonRpc, Calculate_DefaultDelay) uint8_t slot = ParseSlot(response); ASSERT_LE(slot, 6u) << "Slot must be in range [0..6]: " << response; - // Wait for the default-delay (100 ms) calculation to complete, then consume - // the result so the slot is freed before the next test. - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - CallMethod("slotResult@" + std::to_string(slot), "{}", response); + // Poll until the default-delay (100 ms) calculation completes so the slot is + // freed before the next test. + ASSERT_EQ(Core::ERROR_NONE, PollSlotResult(*this, slot, response)) + << "SlotResult not ready within timeout for slot " << +slot; } // @async state — inProgress must report true while the calculation is still running. @@ -110,13 +128,10 @@ TEST_F(TestAsyncJsonRpc, SlotResult_IndexProperty) uint8_t slot = ParseSlot(response); ASSERT_LE(slot, 6u); - // Wait for the 50 ms calculation to finish. - std::this_thread::sleep_for(std::chrono::milliseconds(300)); - - // Read the result via the @index property: slotResult@ + // Poll until the 50 ms calculation finishes rather than relying on a fixed sleep. string resultResponse; - ASSERT_EQ(Core::ERROR_NONE, - CallMethod("slotResult@" + std::to_string(slot), "{}", resultResponse)); + ASSERT_EQ(Core::ERROR_NONE, PollSlotResult(*this, slot, resultResponse)) + << "SlotResult not ready within timeout for slot " << +slot; // impl computes value * 2, so 21 * 2 = 42 EXPECT_NE(resultResponse.find("42"), string::npos) << "Response: " << resultResponse; } From 1982d31735a696cd94ff597deb5c0b00fcd4e4fc Mon Sep 17 00:00:00 2001 From: smanes0213 Date: Mon, 22 Jun 2026 11:29:11 +0530 Subject: [PATCH 27/29] Resolve workflow failure --- tests/FunctionalTests/common/CMakeLists.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/FunctionalTests/common/CMakeLists.txt b/tests/FunctionalTests/common/CMakeLists.txt index b1d089de..6242287c 100644 --- a/tests/FunctionalTests/common/CMakeLists.txt +++ b/tests/FunctionalTests/common/CMakeLists.txt @@ -51,6 +51,13 @@ target_link_libraries(FunctionalTestCommon ${NAMESPACE}COM::${NAMESPACE}COM ) +if(ENABLE_JSON_RPC_TESTS) + target_link_libraries(FunctionalTestCommon + PUBLIC + ${NAMESPACE}Plugins::${NAMESPACE}Plugins + ) +endif() + add_library(FunctionalTestProxySources OBJECT) add_library(FunctionalTestJsonSources OBJECT) From 05a7e085b5e79b92770eedc32bfba352d1b4f6ae Mon Sep 17 00:00:00 2001 From: smanes0213 Date: Mon, 22 Jun 2026 11:35:23 +0530 Subject: [PATCH 28/29] Resolve workflow failure --- tests/FunctionalTests/common/include/ImplementationFactory.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/FunctionalTests/common/include/ImplementationFactory.h b/tests/FunctionalTests/common/include/ImplementationFactory.h index 82078e5b..73f37984 100644 --- a/tests/FunctionalTests/common/include/ImplementationFactory.h +++ b/tests/FunctionalTests/common/include/ImplementationFactory.h @@ -19,7 +19,7 @@ #pragma once -#include +#include "Module.h" #include namespace Thunder { From 4d69c5297bc1db593184c5e3e1b653c888baf987 Mon Sep 17 00:00:00 2001 From: smanes0213 Date: Mon, 22 Jun 2026 11:41:13 +0530 Subject: [PATCH 29/29] Resolve workflow failure --- tests/FunctionalTests/common/include/JsonRpcRegistrations.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/FunctionalTests/common/include/JsonRpcRegistrations.h b/tests/FunctionalTests/common/include/JsonRpcRegistrations.h index ed2d0646..840b22c1 100644 --- a/tests/FunctionalTests/common/include/JsonRpcRegistrations.h +++ b/tests/FunctionalTests/common/include/JsonRpcRegistrations.h @@ -19,7 +19,7 @@ #pragma once -#include +#include "Module.h" namespace Thunder { namespace Test {