From 950891dafd20daf202a4b3ee7eeb5927b8c48234 Mon Sep 17 00:00:00 2001 From: bobra200 Date: Wed, 19 Aug 2026 11:37:18 -0700 Subject: [PATCH 01/12] chore: transport to latest --- .transport.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.transport.version b/.transport.version index e25d8d9..9ee1f78 100644 --- a/.transport.version +++ b/.transport.version @@ -1 +1 @@ -1.1.5 +1.1.11 From 0dccae366f6ec233ea498c083a0808f0f34e6a91 Mon Sep 17 00:00:00 2001 From: bobra200 Date: Wed, 19 Aug 2026 15:24:08 -0700 Subject: [PATCH 02/12] fix: cov_build.sh fixes --- cov_build.sh | 8 ++++- include/firebolt/speechsynthesis.h | 1 + src/json_types/speechsynthesis.h | 2 ++ src/speechsynthesis_impl.h | 2 ++ test/CMakeLists.txt | 48 ++++++++++++++++++++++++++++++ 5 files changed, 60 insertions(+), 1 deletion(-) diff --git a/cov_build.sh b/cov_build.sh index 7e70d6e..6168ea6 100755 --- a/cov_build.sh +++ b/cov_build.sh @@ -137,7 +137,13 @@ bootstrap_transport_if_missing() { echo "Downloaded FireboltTransport release from ${release_url}" fi - release_dir="$(tar -tzf "${release_archive}" | sed -e 's|^\./||' | awk -F/ 'NF{print $1; exit}')" + release_dir="" + while IFS= read -r entry; do + entry="${entry#./}" + if [[ -z "${release_dir}" && -n "${entry}" ]]; then + release_dir="${entry%%/*}" + fi + done < <(tar -tzf "${release_archive}") if [[ -z "${release_dir}" ]]; then echo "Transport archive appears to be empty: ${release_archive}" >&2 return 1 diff --git a/include/firebolt/speechsynthesis.h b/include/firebolt/speechsynthesis.h index a809351..ff19388 100644 --- a/include/firebolt/speechsynthesis.h +++ b/include/firebolt/speechsynthesis.h @@ -20,6 +20,7 @@ #include #include +#include #include #include namespace Firebolt::SpeechSynthesis diff --git a/src/json_types/speechsynthesis.h b/src/json_types/speechsynthesis.h index 00f4993..56ed6a1 100644 --- a/src/json_types/speechsynthesis.h +++ b/src/json_types/speechsynthesis.h @@ -18,6 +18,8 @@ #pragma once +#include + #include "firebolt/speechsynthesis.h" #include diff --git a/src/speechsynthesis_impl.h b/src/speechsynthesis_impl.h index 147d9f7..3b3d87b 100644 --- a/src/speechsynthesis_impl.h +++ b/src/speechsynthesis_impl.h @@ -18,6 +18,8 @@ #pragma once +#include + #include #include diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index bc6eea3..c0f4c71 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -48,6 +48,42 @@ else() find_package(GTest CONFIG REQUIRED) endif() +set(FIREBOLT_GTEST_HEADER_ROOT "") +if(TARGET GTest::gtest) + get_target_property(FIREBOLT_GTEST_INTERFACE_INCLUDE_DIRS GTest::gtest INTERFACE_INCLUDE_DIRECTORIES) + foreach(dir IN LISTS FIREBOLT_GTEST_INTERFACE_INCLUDE_DIRS) + if(EXISTS "${dir}/gtest/gtest.h") + set(FIREBOLT_GTEST_HEADER_ROOT "${dir}") + break() + endif() + endforeach() +endif() + +set(FIREBOLT_GTEST_OVERRIDE_DIR "") +if(FIREBOLT_GTEST_HEADER_ROOT + AND EXISTS "/usr/local/include/gtest/internal/gtest-internal.h" + AND EXISTS "${FIREBOLT_GTEST_HEADER_ROOT}/gtest/internal/gtest-internal.h") + file(STRINGS "/usr/local/include/gtest/internal/gtest-internal.h" FIREBOLT_GTEST_LOCAL_SIGNATURE + REGEX "std::string test_suite_name" LIMIT_COUNT 1) + file(STRINGS "${FIREBOLT_GTEST_HEADER_ROOT}/gtest/internal/gtest-internal.h" FIREBOLT_GTEST_SELECTED_SIGNATURE + REGEX "const char\\* test_suite_name" LIMIT_COUNT 1) + + if(FIREBOLT_GTEST_LOCAL_SIGNATURE AND FIREBOLT_GTEST_SELECTED_SIGNATURE) + set(FIREBOLT_GTEST_OVERRIDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/gtest_header_override") + file(REMOVE_RECURSE "${FIREBOLT_GTEST_OVERRIDE_DIR}") + file(MAKE_DIRECTORY "${FIREBOLT_GTEST_OVERRIDE_DIR}") + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink + "${FIREBOLT_GTEST_HEADER_ROOT}/gtest" + "${FIREBOLT_GTEST_OVERRIDE_DIR}/gtest") + if(EXISTS "${FIREBOLT_GTEST_HEADER_ROOT}/gmock") + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink + "${FIREBOLT_GTEST_HEADER_ROOT}/gmock" + "${FIREBOLT_GTEST_OVERRIDE_DIR}/gmock") + endif() + message(STATUS "Using GTest header override: ${FIREBOLT_GTEST_OVERRIDE_DIR}") + endif() +endif() + find_package(nlohmann_json_schema_validator CONFIG REQUIRED) include(GoogleTest) @@ -67,6 +103,12 @@ add_executable(${UNIT_TESTS_APP} ${UNIT_TESTS} ) +if(FIREBOLT_GTEST_OVERRIDE_DIR) + target_include_directories(${UNIT_TESTS_APP} BEFORE PRIVATE + "${FIREBOLT_GTEST_OVERRIDE_DIR}" + ) +endif() + target_compile_options(${UNIT_TESTS_APP} PRIVATE --coverage -g -O0 -fno-inline) target_link_options(${UNIT_TESTS_APP} PRIVATE --coverage) @@ -115,6 +157,12 @@ add_executable(${COMPONENT_TESTS_APP} ${COMPONENT_TESTS} ) +if(FIREBOLT_GTEST_OVERRIDE_DIR) + target_include_directories(${COMPONENT_TESTS_APP} BEFORE PRIVATE + "${FIREBOLT_GTEST_OVERRIDE_DIR}" + ) +endif() + target_link_libraries(${COMPONENT_TESTS_APP} PRIVATE FireboltClient From 1896572cea82e1a4cc192edef956a555d05a8d27 Mon Sep 17 00:00:00 2001 From: bobra200 Date: Wed, 19 Aug 2026 15:32:31 -0700 Subject: [PATCH 03/12] chore: add missing dep --- test/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c0f4c71..535f08a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -72,11 +72,11 @@ if(FIREBOLT_GTEST_HEADER_ROOT set(FIREBOLT_GTEST_OVERRIDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/gtest_header_override") file(REMOVE_RECURSE "${FIREBOLT_GTEST_OVERRIDE_DIR}") file(MAKE_DIRECTORY "${FIREBOLT_GTEST_OVERRIDE_DIR}") - execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink + execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory "${FIREBOLT_GTEST_HEADER_ROOT}/gtest" "${FIREBOLT_GTEST_OVERRIDE_DIR}/gtest") if(EXISTS "${FIREBOLT_GTEST_HEADER_ROOT}/gmock") - execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink + execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory "${FIREBOLT_GTEST_HEADER_ROOT}/gmock" "${FIREBOLT_GTEST_OVERRIDE_DIR}/gmock") endif() From 254b513a07eb84865955600a336f9996c90c085c Mon Sep 17 00:00:00 2001 From: bobra200 Date: Thu, 3 Sep 2026 17:01:07 -0700 Subject: [PATCH 04/12] nojira: fix videoutput ser/deserialization bug(s) --- docs/openrpc/the-spec/firebolt-open-rpc.json | 297 ++++++++++++++++++- src/json_types/videooutput.h | 104 +++++++ src/videooutput_impl.cpp | 29 +- test/component/videooutputGeneratedTest.cpp | 173 +++++++++++ test/unit/videooutputGeneratedTest.cpp | 29 +- 5 files changed, 595 insertions(+), 37 deletions(-) diff --git a/docs/openrpc/the-spec/firebolt-open-rpc.json b/docs/openrpc/the-spec/firebolt-open-rpc.json index a7a5052..c74dca0 100644 --- a/docs/openrpc/the-spec/firebolt-open-rpc.json +++ b/docs/openrpc/the-spec/firebolt-open-rpc.json @@ -3446,7 +3446,13 @@ "result": { "name": "result", "schema": { - "type": "null" + "type": "string", + "enum": [ + "direct", + "hdcp1.4", + "hdcp2.2", + "none" + ] } } }, @@ -3494,7 +3500,12 @@ "result": { "name": "result", "schema": { - "type": "null" + "type": "string", + "enum": [ + "active", + "inactive", + "unsupported" + ] } } }, @@ -3542,7 +3553,18 @@ "result": { "name": "result", "schema": { - "type": "null" + "type": "string", + "enum": [ + "0", + "23.976", + "24", + "25", + "29.97", + "30", + "50", + "59.94", + "60" + ] } } }, @@ -4112,6 +4134,275 @@ } } }, + { + "name": "VideoOutput.hdcp", + "summary": "Returns HDCP state for the active video output", + "params": [], + "tags": [ + { + "name": "capabilities", + "x-uses": [ + "xrn:firebolt:capability:cpp-client-only" + ] + } + ], + "result": { + "name": "hdcp", + "schema": { + "type": "string", + "enum": [ + "direct", + "hdcp1.4", + "hdcp2.2", + "none" + ] + } + }, + "examples": [ + { + "name": "Getting HDCP state", + "params": [], + "result": { + "name": "Default Result", + "value": "hdcp1.4" + } + } + ] + }, + { + "name": "VideoOutput.cecState", + "summary": "Returns CEC state for the active video output", + "params": [], + "tags": [ + { + "name": "capabilities", + "x-uses": [ + "xrn:firebolt:capability:cpp-client-only" + ] + } + ], + "result": { + "name": "cecState", + "schema": { + "type": "string", + "enum": [ + "active", + "inactive", + "unsupported" + ] + } + }, + "examples": [ + { + "name": "Getting CEC state", + "params": [], + "result": { + "name": "Default Result", + "value": "inactive" + } + } + ] + }, + { + "name": "VideoOutput.refreshRate", + "summary": "Returns refresh rate for the active video output", + "params": [], + "tags": [ + { + "name": "capabilities", + "x-uses": [ + "xrn:firebolt:capability:cpp-client-only" + ] + } + ], + "result": { + "name": "refreshRate", + "schema": { + "type": "string", + "enum": [ + "0", + "23.976", + "24", + "25", + "29.97", + "30", + "50", + "59.94", + "60" + ] + } + }, + "examples": [ + { + "name": "Getting refresh rate", + "params": [], + "result": { + "name": "Default Result", + "value": "59.94" + } + } + ] + }, + { + "name": "VideoOutput.onHdcpChanged", + "summary": "Returns HDCP state changes for the active video output", + "params": [ + { + "name": "listen", + "schema": { + "type": "boolean" + } + } + ], + "tags": [ + { + "name": "event", + "x-notifier": "VideoOutput.onHdcpChanged", + "x-subscriber-for": "VideoOutput.hdcp" + }, + { + "name": "capabilities", + "x-uses": [ + "xrn:firebolt:capability:cpp-client-only" + ] + } + ], + "examples": [ + { + "name": "Getting HDCP state change notifications", + "params": [ + { + "name": "listen", + "value": true + } + ], + "result": { + "name": "result", + "value": null + } + } + ], + "result": { + "name": "result", + "schema": { + "type": "string", + "enum": [ + "direct", + "hdcp1.4", + "hdcp2.2", + "none" + ] + } + } + }, + { + "name": "VideoOutput.onCecStateChanged", + "summary": "Returns CEC state changes for the active video output", + "params": [ + { + "name": "listen", + "schema": { + "type": "boolean" + } + } + ], + "tags": [ + { + "name": "event", + "x-notifier": "VideoOutput.onCecStateChanged", + "x-subscriber-for": "VideoOutput.cecState" + }, + { + "name": "capabilities", + "x-uses": [ + "xrn:firebolt:capability:cpp-client-only" + ] + } + ], + "examples": [ + { + "name": "Getting CEC state change notifications", + "params": [ + { + "name": "listen", + "value": true + } + ], + "result": { + "name": "result", + "value": null + } + } + ], + "result": { + "name": "result", + "schema": { + "type": "string", + "enum": [ + "active", + "inactive", + "unsupported" + ] + } + } + }, + { + "name": "VideoOutput.onRefreshRateChanged", + "summary": "Returns refresh rate changes for the active video output", + "params": [ + { + "name": "listen", + "schema": { + "type": "boolean" + } + } + ], + "tags": [ + { + "name": "event", + "x-notifier": "VideoOutput.onRefreshRateChanged", + "x-subscriber-for": "VideoOutput.refreshRate" + }, + { + "name": "capabilities", + "x-uses": [ + "xrn:firebolt:capability:cpp-client-only" + ] + } + ], + "examples": [ + { + "name": "Getting refresh rate change notifications", + "params": [ + { + "name": "listen", + "value": true + } + ], + "result": { + "name": "result", + "value": null + } + } + ], + "result": { + "name": "result", + "schema": { + "type": "string", + "enum": [ + "0", + "23.976", + "24", + "25", + "29.97", + "30", + "50", + "59.94", + "60" + ] + } + } + }, { "name": "Localization.onCountryChanged", "tags": [ diff --git a/src/json_types/videooutput.h b/src/json_types/videooutput.h index 9cc1398..9409da9 100644 --- a/src/json_types/videooutput.h +++ b/src/json_types/videooutput.h @@ -155,6 +155,110 @@ inline const Firebolt::JSON::EnumType<::Firebolt::VideoOutput::RefreshRateValue> {"60", ::Firebolt::VideoOutput::RefreshRateValue::R60}, }); +class CecStateValueJson : public Firebolt::JSON::NL_Json_Basic<::Firebolt::VideoOutput::CecStateValue> +{ +public: + void fromJson(const nlohmann::json& json) override + { + cecStateValue_ = CecStateValueEnum.at(json.get()); + } + [[nodiscard]] ::Firebolt::VideoOutput::CecStateValue value() const override { return cecStateValue_; } + +private: + ::Firebolt::VideoOutput::CecStateValue cecStateValue_; +}; + +class ColorDepthValueJson : public Firebolt::JSON::NL_Json_Basic<::Firebolt::VideoOutput::ColorDepthValue> +{ +public: + void fromJson(const nlohmann::json& json) override + { + colorDepthValue_ = ColorDepthValueEnum.at(json.get()); + } + [[nodiscard]] ::Firebolt::VideoOutput::ColorDepthValue value() const override { return colorDepthValue_; } + +private: + ::Firebolt::VideoOutput::ColorDepthValue colorDepthValue_; +}; + +class ColorFormatValueJson : public Firebolt::JSON::NL_Json_Basic<::Firebolt::VideoOutput::ColorFormatValue> +{ +public: + void fromJson(const nlohmann::json& json) override + { + colorFormatValue_ = ColorFormatValueEnum.at(json.get()); + } + [[nodiscard]] ::Firebolt::VideoOutput::ColorFormatValue value() const override { return colorFormatValue_; } + +private: + ::Firebolt::VideoOutput::ColorFormatValue colorFormatValue_; +}; + +class DynamicRangeValueJson : public Firebolt::JSON::NL_Json_Basic<::Firebolt::VideoOutput::DynamicRangeValue> +{ +public: + void fromJson(const nlohmann::json& json) override + { + dynamicRangeValue_ = DynamicRangeValueEnum.at(json.get()); + } + [[nodiscard]] ::Firebolt::VideoOutput::DynamicRangeValue value() const override { return dynamicRangeValue_; } + +private: + ::Firebolt::VideoOutput::DynamicRangeValue dynamicRangeValue_; +}; + +class HdcpStateJson : public Firebolt::JSON::NL_Json_Basic<::Firebolt::VideoOutput::HdcpState> +{ +public: + void fromJson(const nlohmann::json& json) override { hdcpState_ = HdcpStateEnum.at(json.get()); } + [[nodiscard]] ::Firebolt::VideoOutput::HdcpState value() const override { return hdcpState_; } + +private: + ::Firebolt::VideoOutput::HdcpState hdcpState_; +}; + +class OutputColorimetryJson : public Firebolt::JSON::NL_Json_Basic<::Firebolt::VideoOutput::OutputColorimetry> +{ +public: + void fromJson(const nlohmann::json& json) override + { + outputColorimetry_ = OutputColorimetryEnum.at(json.get()); + } + [[nodiscard]] ::Firebolt::VideoOutput::OutputColorimetry value() const override { return outputColorimetry_; } + +private: + ::Firebolt::VideoOutput::OutputColorimetry outputColorimetry_; +}; + +class QuantizationRangeValueJson : public Firebolt::JSON::NL_Json_Basic<::Firebolt::VideoOutput::QuantizationRangeValue> +{ +public: + void fromJson(const nlohmann::json& json) override + { + quantizationRangeValue_ = QuantizationRangeValueEnum.at(json.get()); + } + [[nodiscard]] ::Firebolt::VideoOutput::QuantizationRangeValue value() const override + { + return quantizationRangeValue_; + } + +private: + ::Firebolt::VideoOutput::QuantizationRangeValue quantizationRangeValue_; +}; + +class RefreshRateValueJson : public Firebolt::JSON::NL_Json_Basic<::Firebolt::VideoOutput::RefreshRateValue> +{ +public: + void fromJson(const nlohmann::json& json) override + { + refreshRateValue_ = RefreshRateValueEnum.at(json.get()); + } + [[nodiscard]] ::Firebolt::VideoOutput::RefreshRateValue value() const override { return refreshRateValue_; } + +private: + ::Firebolt::VideoOutput::RefreshRateValue refreshRateValue_; +}; + class VideoOutputResolution : public Firebolt::JSON::NL_Json_Basic<::Firebolt::VideoOutput::VideoOutputResolution> { public: diff --git a/src/videooutput_impl.cpp b/src/videooutput_impl.cpp index 73242b3..b8370eb 100644 --- a/src/videooutput_impl.cpp +++ b/src/videooutput_impl.cpp @@ -44,60 +44,57 @@ VideoOutputImpl::subscribeOnResolutionChanged(std::function VideoOutputImpl::hdcp() const { - return helper_.get, HdcpState>("VideoOutput.hdcp"); + return helper_.get("VideoOutput.hdcp"); } Result VideoOutputImpl::subscribeOnHdcpChanged(std::function&& notification) { - return subscriptionManager_.subscribe>("VideoOutput.onHdcpChanged", - std::move(notification)); + return subscriptionManager_.subscribe("VideoOutput.onHdcpChanged", std::move(notification)); } Result VideoOutputImpl::cecState() const { - return helper_.get, CecStateValue>("VideoOutput.cecState"); + return helper_.get("VideoOutput.cecState"); } Result VideoOutputImpl::subscribeOnCecStateChanged(std::function&& notification) { - return subscriptionManager_.subscribe>("VideoOutput.onCecStateChanged", - std::move(notification)); + return subscriptionManager_.subscribe("VideoOutput.onCecStateChanged", + std::move(notification)); } Result VideoOutputImpl::refreshRate() const { - return helper_.get, RefreshRateValue>("VideoOutput.refreshRate"); + return helper_.get("VideoOutput.refreshRate"); } Result VideoOutputImpl::subscribeOnRefreshRateChanged(std::function&& notification) { - return subscriptionManager_ - .subscribe>("VideoOutput.onRefreshRateChanged", - std::move(notification)); + return subscriptionManager_.subscribe("VideoOutput.onRefreshRateChanged", + std::move(notification)); } Result VideoOutputImpl::colorDepth() const { - return helper_.get, ColorDepthValue>("VideoOutput.colorDepth"); + return helper_.get("VideoOutput.colorDepth"); } Result VideoOutputImpl::colorFormat() const { - return helper_.get, ColorFormatValue>("VideoOutput.colorFormat"); + return helper_.get("VideoOutput.colorFormat"); } Result VideoOutputImpl::colorimetry() const { - return helper_.get, OutputColorimetry>("VideoOutput.colorimetry"); + return helper_.get("VideoOutput.colorimetry"); } Result VideoOutputImpl::dynamicRange() const { - return helper_.get, DynamicRangeValue>("VideoOutput.dynamicRange"); + return helper_.get("VideoOutput.dynamicRange"); } Result VideoOutputImpl::quantizationRange() const { - return helper_.get, QuantizationRangeValue>( - "VideoOutput.quantizationRange"); + return helper_.get("VideoOutput.quantizationRange"); } Result VideoOutputImpl::unsubscribe(SubscriptionId id) diff --git a/test/component/videooutputGeneratedTest.cpp b/test/component/videooutputGeneratedTest.cpp index b2f9958..7b4ea4d 100644 --- a/test/component/videooutputGeneratedTest.cpp +++ b/test/component/videooutputGeneratedTest.cpp @@ -16,8 +16,94 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include "firebolt/firebolt.h" #include "firebolt/videooutput.h" +#include "json_types/videooutput.h" +#include "utils.h" +#include #include +#include + +class VideooutputGeneratedRuntimeCTest : public ::testing::Test +{ +protected: + void SetUp() override { eventReceived = false; } + + void resetEventState() + { + std::lock_guard lock(mtx); + eventReceived = false; + } + + std::condition_variable cv; + std::mutex mtx; + bool eventReceived{false}; +}; + +TEST(VideooutputGeneratedCTest, HdcpMarshallerParsesWireString) +{ + Firebolt::VideoOutput::JsonData::HdcpStateJson jsonType; + jsonType.fromJson(nlohmann::json("hdcp1.4")); + + EXPECT_EQ(jsonType.value(), Firebolt::VideoOutput::HdcpState::Hdcp14); +} + +TEST(VideooutputGeneratedCTest, CecStateMarshallerParsesWireString) +{ + Firebolt::VideoOutput::JsonData::CecStateValueJson jsonType; + jsonType.fromJson(nlohmann::json("inactive")); + + EXPECT_EQ(jsonType.value(), Firebolt::VideoOutput::CecStateValue::Inactive); +} + +TEST(VideooutputGeneratedCTest, ColorFormatMarshallerParsesWireString) +{ + Firebolt::VideoOutput::JsonData::ColorFormatValueJson jsonType; + jsonType.fromJson(nlohmann::json("ycbcr422")); + + EXPECT_EQ(jsonType.value(), Firebolt::VideoOutput::ColorFormatValue::Ycbcr422); +} + +TEST(VideooutputGeneratedCTest, DynamicRangeMarshallerParsesWireString) +{ + Firebolt::VideoOutput::JsonData::DynamicRangeValueJson jsonType; + jsonType.fromJson(nlohmann::json("sdr")); + + EXPECT_EQ(jsonType.value(), Firebolt::VideoOutput::DynamicRangeValue::Sdr); +} + +TEST(VideooutputGeneratedCTest, QuantizationRangeMarshallerParsesWireString) +{ + Firebolt::VideoOutput::JsonData::QuantizationRangeValueJson jsonType; + jsonType.fromJson(nlohmann::json("limited")); + + EXPECT_EQ(jsonType.value(), Firebolt::VideoOutput::QuantizationRangeValue::Limited); +} + +TEST(VideooutputGeneratedCTest, RefreshRateMarshallerParsesWireString) +{ + Firebolt::VideoOutput::JsonData::RefreshRateValueJson jsonType; + jsonType.fromJson(nlohmann::json("59.94")); + + EXPECT_EQ(jsonType.value(), Firebolt::VideoOutput::RefreshRateValue::R5994); +} + +TEST(VideooutputGeneratedCTest, MarshallersRejectUnknownWireValues) +{ + Firebolt::VideoOutput::JsonData::HdcpStateJson hdcpJson; + Firebolt::VideoOutput::JsonData::CecStateValueJson cecStateJson; + Firebolt::VideoOutput::JsonData::ColorFormatValueJson colorFormatJson; + Firebolt::VideoOutput::JsonData::DynamicRangeValueJson dynamicRangeJson; + Firebolt::VideoOutput::JsonData::QuantizationRangeValueJson quantizationRangeJson; + Firebolt::VideoOutput::JsonData::RefreshRateValueJson refreshRateJson; + + EXPECT_THROW(hdcpJson.fromJson(nlohmann::json("hdcp3.0")), std::out_of_range); + EXPECT_THROW(cecStateJson.fromJson(nlohmann::json("not-a-state")), std::out_of_range); + EXPECT_THROW(colorFormatJson.fromJson(nlohmann::json("xyz")), std::out_of_range); + EXPECT_THROW(dynamicRangeJson.fromJson(nlohmann::json("hdr11")), std::out_of_range); + EXPECT_THROW(quantizationRangeJson.fromJson(nlohmann::json("super")), std::out_of_range); + EXPECT_THROW(refreshRateJson.fromJson(nlohmann::json("61")), std::out_of_range); +} TEST(VideooutputGeneratedCTest, InterfaceSurfaceHasresolution) { @@ -34,3 +120,90 @@ TEST(VideooutputGeneratedCTest, InterfaceSurfaceHascolorDepth) (void)ptr; SUCCEED(); } + +TEST_F(VideooutputGeneratedRuntimeCTest, SubscribeOnHdcpChangedParsesWireStringPayload) +{ + auto id = Firebolt::IFireboltAccessor::Instance().VideoOutputInterface().subscribeOnHdcpChanged( + [&](const Firebolt::VideoOutput::HdcpState& value) + { + EXPECT_EQ(value, Firebolt::VideoOutput::HdcpState::Hdcp14); + { + std::lock_guard lock(mtx); + eventReceived = true; + } + cv.notify_one(); + }); + + if (!id) + { + GTEST_SKIP() << "VideoOutput.onHdcpChanged is not available in the current mock OpenRPC fixture"; + } + verifyEventSubscription(id); + triggerEvent("VideoOutput.onHdcpChanged", R"("hdcp1.4")"); + verifyEventReceived(mtx, cv, eventReceived); + + resetEventState(); + triggerEvent("VideoOutput.onHdcpChanged", R"("invalid-hdcp")"); + verifyEventNotReceived(mtx, cv, eventReceived); + + auto result = Firebolt::IFireboltAccessor::Instance().VideoOutputInterface().unsubscribe(id.value()); + verifyUnsubscribeResult(result); +} + +TEST_F(VideooutputGeneratedRuntimeCTest, SubscribeOnCecStateChangedParsesWireStringPayload) +{ + auto id = Firebolt::IFireboltAccessor::Instance().VideoOutputInterface().subscribeOnCecStateChanged( + [&](const Firebolt::VideoOutput::CecStateValue& value) + { + EXPECT_EQ(value, Firebolt::VideoOutput::CecStateValue::Inactive); + { + std::lock_guard lock(mtx); + eventReceived = true; + } + cv.notify_one(); + }); + + if (!id) + { + GTEST_SKIP() << "VideoOutput.onCecStateChanged is not available in the current mock OpenRPC fixture"; + } + verifyEventSubscription(id); + triggerEvent("VideoOutput.onCecStateChanged", R"("inactive")"); + verifyEventReceived(mtx, cv, eventReceived); + + resetEventState(); + triggerEvent("VideoOutput.onCecStateChanged", R"("invalid-cec")"); + verifyEventNotReceived(mtx, cv, eventReceived); + + auto result = Firebolt::IFireboltAccessor::Instance().VideoOutputInterface().unsubscribe(id.value()); + verifyUnsubscribeResult(result); +} + +TEST_F(VideooutputGeneratedRuntimeCTest, SubscribeOnRefreshRateChangedParsesWireStringPayload) +{ + auto id = Firebolt::IFireboltAccessor::Instance().VideoOutputInterface().subscribeOnRefreshRateChanged( + [&](const Firebolt::VideoOutput::RefreshRateValue& value) + { + EXPECT_EQ(value, Firebolt::VideoOutput::RefreshRateValue::R5994); + { + std::lock_guard lock(mtx); + eventReceived = true; + } + cv.notify_one(); + }); + + if (!id) + { + GTEST_SKIP() << "VideoOutput.onRefreshRateChanged is not available in the current mock OpenRPC fixture"; + } + verifyEventSubscription(id); + triggerEvent("VideoOutput.onRefreshRateChanged", R"("59.94")"); + verifyEventReceived(mtx, cv, eventReceived); + + resetEventState(); + triggerEvent("VideoOutput.onRefreshRateChanged", R"("61")"); + verifyEventNotReceived(mtx, cv, eventReceived); + + auto result = Firebolt::IFireboltAccessor::Instance().VideoOutputInterface().unsubscribe(id.value()); + verifyUnsubscribeResult(result); +} diff --git a/test/unit/videooutputGeneratedTest.cpp b/test/unit/videooutputGeneratedTest.cpp index 736ac6c..5a8a462 100644 --- a/test/unit/videooutputGeneratedTest.cpp +++ b/test/unit/videooutputGeneratedTest.cpp @@ -114,7 +114,7 @@ TEST_F(VideooutputGeneratedUTest, ResolutionReturnsInvalidParamsWhenPayloadIsMal TEST_F(VideooutputGeneratedUTest, HdcpReturnsParsedValue) { - expectGetterResponse("VideoOutput.hdcp", nlohmann::json(static_cast(Firebolt::VideoOutput::HdcpState::Hdcp22))); + expectGetterResponse("VideoOutput.hdcp", nlohmann::json("hdcp2.2")); auto result = impl.hdcp(); ASSERT_TRUE(result); @@ -132,8 +132,7 @@ TEST_F(VideooutputGeneratedUTest, HdcpForwardsTransportErrors) TEST_F(VideooutputGeneratedUTest, CecStateReturnsParsedValue) { - expectGetterResponse("VideoOutput.cecState", - nlohmann::json(static_cast(Firebolt::VideoOutput::CecStateValue::Inactive))); + expectGetterResponse("VideoOutput.cecState", nlohmann::json("inactive")); auto result = impl.cecState(); ASSERT_TRUE(result); @@ -151,8 +150,7 @@ TEST_F(VideooutputGeneratedUTest, CecStateForwardsTransportErrors) TEST_F(VideooutputGeneratedUTest, RefreshRateReturnsParsedValue) { - expectGetterResponse("VideoOutput.refreshRate", - nlohmann::json(static_cast(Firebolt::VideoOutput::RefreshRateValue::R5994))); + expectGetterResponse("VideoOutput.refreshRate", nlohmann::json("59.94")); auto result = impl.refreshRate(); ASSERT_TRUE(result); @@ -170,8 +168,7 @@ TEST_F(VideooutputGeneratedUTest, RefreshRateForwardsTransportErrors) TEST_F(VideooutputGeneratedUTest, ColorDepthReturnsParsedValue) { - expectGetterResponse("VideoOutput.colorDepth", - nlohmann::json(static_cast(Firebolt::VideoOutput::ColorDepthValue::D12))); + expectGetterResponse("VideoOutput.colorDepth", nlohmann::json("12")); auto result = impl.colorDepth(); ASSERT_TRUE(result); @@ -189,8 +186,7 @@ TEST_F(VideooutputGeneratedUTest, ColorDepthForwardsTransportErrors) TEST_F(VideooutputGeneratedUTest, ColorFormatReturnsParsedValue) { - expectGetterResponse("VideoOutput.colorFormat", - nlohmann::json(static_cast(Firebolt::VideoOutput::ColorFormatValue::Ycbcr444))); + expectGetterResponse("VideoOutput.colorFormat", nlohmann::json("ycbcr444")); auto result = impl.colorFormat(); ASSERT_TRUE(result); @@ -208,8 +204,7 @@ TEST_F(VideooutputGeneratedUTest, ColorFormatForwardsTransportErrors) TEST_F(VideooutputGeneratedUTest, ColorimetryReturnsParsedValue) { - expectGetterResponse("VideoOutput.colorimetry", - nlohmann::json(static_cast(Firebolt::VideoOutput::OutputColorimetry::Bt2020rgb))); + expectGetterResponse("VideoOutput.colorimetry", nlohmann::json("bt2020rgb")); auto result = impl.colorimetry(); ASSERT_TRUE(result); @@ -227,8 +222,7 @@ TEST_F(VideooutputGeneratedUTest, ColorimetryForwardsTransportErrors) TEST_F(VideooutputGeneratedUTest, DynamicRangeReturnsParsedValue) { - expectGetterResponse("VideoOutput.dynamicRange", - nlohmann::json(static_cast(Firebolt::VideoOutput::DynamicRangeValue::Hdr10plus))); + expectGetterResponse("VideoOutput.dynamicRange", nlohmann::json("hdr10plus")); auto result = impl.dynamicRange(); ASSERT_TRUE(result); @@ -246,8 +240,7 @@ TEST_F(VideooutputGeneratedUTest, DynamicRangeForwardsTransportErrors) TEST_F(VideooutputGeneratedUTest, QuantizationRangeReturnsParsedValue) { - expectGetterResponse("VideoOutput.quantizationRange", - nlohmann::json(static_cast(Firebolt::VideoOutput::QuantizationRangeValue::Limited))); + expectGetterResponse("VideoOutput.quantizationRange", nlohmann::json("limited")); auto result = impl.quantizationRange(); ASSERT_TRUE(result); @@ -336,7 +329,7 @@ TEST_F(VideooutputGeneratedUTest, SubscribeOnHdcpChangedForwardsAndDispatchesPar void (*callback)(void*, const nlohmann::json&)) { Firebolt::Helpers::SubscriptionData data{owner, eventName, std::move(notification)}; - callback(&data, nlohmann::json(static_cast(Firebolt::VideoOutput::HdcpState::Direct))); + callback(&data, nlohmann::json("direct")); return Firebolt::Result{11}; })); @@ -375,7 +368,7 @@ TEST_F(VideooutputGeneratedUTest, SubscribeOnCecStateChangedForwardsAndDispatche void (*callback)(void*, const nlohmann::json&)) { Firebolt::Helpers::SubscriptionData data{owner, eventName, std::move(notification)}; - callback(&data, nlohmann::json(static_cast(Firebolt::VideoOutput::CecStateValue::Active))); + callback(&data, nlohmann::json("active")); return Firebolt::Result{12}; })); @@ -414,7 +407,7 @@ TEST_F(VideooutputGeneratedUTest, SubscribeOnRefreshRateChangedForwardsAndDispat void (*callback)(void*, const nlohmann::json&)) { Firebolt::Helpers::SubscriptionData data{owner, eventName, std::move(notification)}; - callback(&data, nlohmann::json(static_cast(Firebolt::VideoOutput::RefreshRateValue::R24))); + callback(&data, nlohmann::json("24")); return Firebolt::Result{13}; })); From f891f07450e415e97891fc5edee8b9fdb13e66c6 Mon Sep 17 00:00:00 2001 From: Brendan O'Bra Date: Fri, 4 Sep 2026 06:36:36 -0700 Subject: [PATCH 05/12] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- test/component/videooutputGeneratedTest.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/component/videooutputGeneratedTest.cpp b/test/component/videooutputGeneratedTest.cpp index 7b4ea4d..9d193d7 100644 --- a/test/component/videooutputGeneratedTest.cpp +++ b/test/component/videooutputGeneratedTest.cpp @@ -134,10 +134,6 @@ TEST_F(VideooutputGeneratedRuntimeCTest, SubscribeOnHdcpChangedParsesWireStringP cv.notify_one(); }); - if (!id) - { - GTEST_SKIP() << "VideoOutput.onHdcpChanged is not available in the current mock OpenRPC fixture"; - } verifyEventSubscription(id); triggerEvent("VideoOutput.onHdcpChanged", R"("hdcp1.4")"); verifyEventReceived(mtx, cv, eventReceived); From 9f3c1e091e640409a15e9941016a533c44d6a6b8 Mon Sep 17 00:00:00 2001 From: bobra200 Date: Fri, 4 Sep 2026 07:08:36 -0700 Subject: [PATCH 06/12] nojira: fix json --- docs/openrpc/the-spec/firebolt-open-rpc.json | 28 +++----------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/docs/openrpc/the-spec/firebolt-open-rpc.json b/docs/openrpc/the-spec/firebolt-open-rpc.json index c74dca0..98a97f7 100644 --- a/docs/openrpc/the-spec/firebolt-open-rpc.json +++ b/docs/openrpc/the-spec/firebolt-open-rpc.json @@ -3446,13 +3446,7 @@ "result": { "name": "result", "schema": { - "type": "string", - "enum": [ - "direct", - "hdcp1.4", - "hdcp2.2", - "none" - ] + "type": "null" } } }, @@ -3500,12 +3494,7 @@ "result": { "name": "result", "schema": { - "type": "string", - "enum": [ - "active", - "inactive", - "unsupported" - ] + "type": "null" } } }, @@ -3553,18 +3542,7 @@ "result": { "name": "result", "schema": { - "type": "string", - "enum": [ - "0", - "23.976", - "24", - "25", - "29.97", - "30", - "50", - "59.94", - "60" - ] + "type": "null" } } }, From 4b9f1e30aebf4e887462c8a93a604bd25403cf39 Mon Sep 17 00:00:00 2001 From: Brendan O'Bra Date: Fri, 4 Sep 2026 07:58:11 -0700 Subject: [PATCH 07/12] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- test/component/videooutputGeneratedTest.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/component/videooutputGeneratedTest.cpp b/test/component/videooutputGeneratedTest.cpp index 9d193d7..86ed38f 100644 --- a/test/component/videooutputGeneratedTest.cpp +++ b/test/component/videooutputGeneratedTest.cpp @@ -159,10 +159,6 @@ TEST_F(VideooutputGeneratedRuntimeCTest, SubscribeOnCecStateChangedParsesWireStr cv.notify_one(); }); - if (!id) - { - GTEST_SKIP() << "VideoOutput.onCecStateChanged is not available in the current mock OpenRPC fixture"; - } verifyEventSubscription(id); triggerEvent("VideoOutput.onCecStateChanged", R"("inactive")"); verifyEventReceived(mtx, cv, eventReceived); From 947ab32e8da0f0eb1f255d482c224a581cd95d7f Mon Sep 17 00:00:00 2001 From: Brendan O'Bra Date: Fri, 4 Sep 2026 07:58:27 -0700 Subject: [PATCH 08/12] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- docs/openrpc/the-spec/firebolt-open-rpc.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/openrpc/the-spec/firebolt-open-rpc.json b/docs/openrpc/the-spec/firebolt-open-rpc.json index 98a97f7..04681d4 100644 --- a/docs/openrpc/the-spec/firebolt-open-rpc.json +++ b/docs/openrpc/the-spec/firebolt-open-rpc.json @@ -4117,6 +4117,9 @@ "summary": "Returns HDCP state for the active video output", "params": [], "tags": [ + { + "name": "property:readonly" + }, { "name": "capabilities", "x-uses": [ From f703838baa2a9ab80e130ab5c598e698233d5a64 Mon Sep 17 00:00:00 2001 From: Brendan O'Bra Date: Fri, 4 Sep 2026 07:58:35 -0700 Subject: [PATCH 09/12] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- test/component/videooutputGeneratedTest.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/component/videooutputGeneratedTest.cpp b/test/component/videooutputGeneratedTest.cpp index 86ed38f..b9b8d77 100644 --- a/test/component/videooutputGeneratedTest.cpp +++ b/test/component/videooutputGeneratedTest.cpp @@ -184,10 +184,6 @@ TEST_F(VideooutputGeneratedRuntimeCTest, SubscribeOnRefreshRateChangedParsesWire cv.notify_one(); }); - if (!id) - { - GTEST_SKIP() << "VideoOutput.onRefreshRateChanged is not available in the current mock OpenRPC fixture"; - } verifyEventSubscription(id); triggerEvent("VideoOutput.onRefreshRateChanged", R"("59.94")"); verifyEventReceived(mtx, cv, eventReceived); From b463ed8e145c0476eca23b42d09ce2115dd9b7d0 Mon Sep 17 00:00:00 2001 From: Brendan O'Bra Date: Wed, 9 Sep 2026 06:34:02 -0700 Subject: [PATCH 10/12] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- docs/openrpc/the-spec/firebolt-open-rpc.json | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/docs/openrpc/the-spec/firebolt-open-rpc.json b/docs/openrpc/the-spec/firebolt-open-rpc.json index 04681d4..3793b1e 100644 --- a/docs/openrpc/the-spec/firebolt-open-rpc.json +++ b/docs/openrpc/the-spec/firebolt-open-rpc.json @@ -4266,13 +4266,7 @@ "result": { "name": "result", "schema": { - "type": "string", - "enum": [ - "direct", - "hdcp1.4", - "hdcp2.2", - "none" - ] + "type": "null" } } }, From cbd81fe42a53e38b54429c2b55e7ea1be7f2c7f9 Mon Sep 17 00:00:00 2001 From: bobra200 Date: Wed, 9 Sep 2026 07:48:00 -0700 Subject: [PATCH 11/12] fix: tighten up videooutput parser ut --- docs/openrpc/the-spec/firebolt-open-rpc.json | 6 ++++++ test/unit/videooutputGeneratedTest.cpp | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/docs/openrpc/the-spec/firebolt-open-rpc.json b/docs/openrpc/the-spec/firebolt-open-rpc.json index 04681d4..67a57cd 100644 --- a/docs/openrpc/the-spec/firebolt-open-rpc.json +++ b/docs/openrpc/the-spec/firebolt-open-rpc.json @@ -4155,6 +4155,9 @@ "summary": "Returns CEC state for the active video output", "params": [], "tags": [ + { + "name": "property:readonly" + }, { "name": "capabilities", "x-uses": [ @@ -4189,6 +4192,9 @@ "summary": "Returns refresh rate for the active video output", "params": [], "tags": [ + { + "name": "property:readonly" + }, { "name": "capabilities", "x-uses": [ diff --git a/test/unit/videooutputGeneratedTest.cpp b/test/unit/videooutputGeneratedTest.cpp index 5a8a462..1cc09be 100644 --- a/test/unit/videooutputGeneratedTest.cpp +++ b/test/unit/videooutputGeneratedTest.cpp @@ -130,6 +130,15 @@ TEST_F(VideooutputGeneratedUTest, HdcpForwardsTransportErrors) EXPECT_EQ(result.error(), Firebolt::Error::General); } +TEST_F(VideooutputGeneratedUTest, HdcpReturnsInvalidParamsWhenEnumValueIsUnknown) +{ + expectGetterResponse("VideoOutput.hdcp", nlohmann::json("hdcp3.0")); + + auto result = impl.hdcp(); + ASSERT_FALSE(result); + EXPECT_EQ(result.error(), Firebolt::Error::InvalidParams); +} + TEST_F(VideooutputGeneratedUTest, CecStateReturnsParsedValue) { expectGetterResponse("VideoOutput.cecState", nlohmann::json("inactive")); From cf7244317bffbbe98792630f8f02982c768a4aae Mon Sep 17 00:00:00 2001 From: bobra200 Date: Wed, 9 Sep 2026 10:47:53 -0700 Subject: [PATCH 12/12] RDKEMW-2473: more fixes --- src/json_types/videooutput.h | 91 +++++++++++++++++++++++++- src/videooutput_impl.cpp | 16 ++++- test/unit/videooutputGeneratedTest.cpp | 63 ++++++++++++++++++ 3 files changed, 166 insertions(+), 4 deletions(-) diff --git a/src/json_types/videooutput.h b/src/json_types/videooutput.h index 9409da9..136993b 100644 --- a/src/json_types/videooutput.h +++ b/src/json_types/videooutput.h @@ -23,6 +23,7 @@ #define FIREBOLT_VIDEOOUTPUT_JSON_H #include "firebolt/videooutput.h" +#include #include #include @@ -173,7 +174,35 @@ class ColorDepthValueJson : public Firebolt::JSON::NL_Json_Basic<::Firebolt::Vid public: void fromJson(const nlohmann::json& json) override { - colorDepthValue_ = ColorDepthValueEnum.at(json.get()); + if (json.is_string()) + { + colorDepthValue_ = ColorDepthValueEnum.at(json.get()); + return; + } + + if (json.is_number_integer() || json.is_number_unsigned()) + { + const auto value = json.get(); + switch (value) + { + case 0: + colorDepthValue_ = ::Firebolt::VideoOutput::ColorDepthValue::D0; + return; + case 8: + colorDepthValue_ = ::Firebolt::VideoOutput::ColorDepthValue::D8; + return; + case 10: + colorDepthValue_ = ::Firebolt::VideoOutput::ColorDepthValue::D10; + return; + case 12: + colorDepthValue_ = ::Firebolt::VideoOutput::ColorDepthValue::D12; + return; + default: + throw std::out_of_range("Unsupported color depth value"); + } + } + + throw std::invalid_argument("ColorDepthValue must be a string or integer"); } [[nodiscard]] ::Firebolt::VideoOutput::ColorDepthValue value() const override { return colorDepthValue_; } @@ -251,7 +280,65 @@ class RefreshRateValueJson : public Firebolt::JSON::NL_Json_Basic<::Firebolt::Vi public: void fromJson(const nlohmann::json& json) override { - refreshRateValue_ = RefreshRateValueEnum.at(json.get()); + if (json.is_string()) + { + refreshRateValue_ = RefreshRateValueEnum.at(json.get()); + return; + } + + if (json.is_number_integer() || json.is_number_unsigned()) + { + const auto value = json.get(); + switch (value) + { + case 0: + refreshRateValue_ = ::Firebolt::VideoOutput::RefreshRateValue::R0; + return; + case 24: + refreshRateValue_ = ::Firebolt::VideoOutput::RefreshRateValue::R24; + return; + case 25: + refreshRateValue_ = ::Firebolt::VideoOutput::RefreshRateValue::R25; + return; + case 30: + refreshRateValue_ = ::Firebolt::VideoOutput::RefreshRateValue::R30; + return; + case 50: + refreshRateValue_ = ::Firebolt::VideoOutput::RefreshRateValue::R50; + return; + case 59: + refreshRateValue_ = ::Firebolt::VideoOutput::RefreshRateValue::R5994; + return; + case 60: + refreshRateValue_ = ::Firebolt::VideoOutput::RefreshRateValue::R60; + return; + default: + throw std::out_of_range("Unsupported refresh rate value"); + } + } + + if (json.is_number_float()) + { + const auto value = json.get(); + if (std::fabs(value - 23.976) < 0.001) + { + refreshRateValue_ = ::Firebolt::VideoOutput::RefreshRateValue::R23976; + return; + } + if (std::fabs(value - 29.97) < 0.001) + { + refreshRateValue_ = ::Firebolt::VideoOutput::RefreshRateValue::R2997; + return; + } + if (std::fabs(value - 59.94) < 0.001) + { + refreshRateValue_ = ::Firebolt::VideoOutput::RefreshRateValue::R5994; + return; + } + throw std::out_of_range("Unsupported refresh rate value"); + } + + throw std::invalid_argument("RefreshRateValue must be a string or number"); } [[nodiscard]] ::Firebolt::VideoOutput::RefreshRateValue value() const override { return refreshRateValue_; } diff --git a/src/videooutput_impl.cpp b/src/videooutput_impl.cpp index b8370eb..4e5c596 100644 --- a/src/videooutput_impl.cpp +++ b/src/videooutput_impl.cpp @@ -68,8 +68,20 @@ Result VideoOutputImpl::refreshRate() const Result VideoOutputImpl::subscribeOnRefreshRateChanged(std::function&& notification) { - return subscriptionManager_.subscribe("VideoOutput.onRefreshRateChanged", - std::move(notification)); + std::function callback = std::move(notification); + + auto result = + subscriptionManager_.subscribe("VideoOutput.onRefreshRateChanged", + std::function( + callback)); + if (!result && result.error() == Firebolt::Error::CapabilityNotSupported) + { + // Compatibility fallback for runtimes exposing a misspelled method alias. + return subscriptionManager_.subscribe("VideoOutput.onRefereshRateChanged", + std::move(callback)); + } + + return result; } Result VideoOutputImpl::colorDepth() const diff --git a/test/unit/videooutputGeneratedTest.cpp b/test/unit/videooutputGeneratedTest.cpp index 1cc09be..ba1840a 100644 --- a/test/unit/videooutputGeneratedTest.cpp +++ b/test/unit/videooutputGeneratedTest.cpp @@ -166,6 +166,24 @@ TEST_F(VideooutputGeneratedUTest, RefreshRateReturnsParsedValue) EXPECT_EQ(*result, Firebolt::VideoOutput::RefreshRateValue::R5994); } +TEST_F(VideooutputGeneratedUTest, RefreshRateReturnsParsedValueWhenPayloadIsInteger) +{ + expectGetterResponse("VideoOutput.refreshRate", nlohmann::json(59)); + + auto result = impl.refreshRate(); + ASSERT_TRUE(result); + EXPECT_EQ(*result, Firebolt::VideoOutput::RefreshRateValue::R5994); +} + +TEST_F(VideooutputGeneratedUTest, RefreshRateReturnsInvalidParamsWhenNumericValueIsUnknown) +{ + expectGetterResponse("VideoOutput.refreshRate", nlohmann::json(61)); + + auto result = impl.refreshRate(); + ASSERT_FALSE(result); + EXPECT_EQ(result.error(), Firebolt::Error::InvalidParams); +} + TEST_F(VideooutputGeneratedUTest, RefreshRateForwardsTransportErrors) { expectGetterTransportError("VideoOutput.refreshRate"); @@ -184,6 +202,24 @@ TEST_F(VideooutputGeneratedUTest, ColorDepthReturnsParsedValue) EXPECT_EQ(*result, Firebolt::VideoOutput::ColorDepthValue::D12); } +TEST_F(VideooutputGeneratedUTest, ColorDepthReturnsParsedValueWhenPayloadIsNumber) +{ + expectGetterResponse("VideoOutput.colorDepth", nlohmann::json(8)); + + auto result = impl.colorDepth(); + ASSERT_TRUE(result); + EXPECT_EQ(*result, Firebolt::VideoOutput::ColorDepthValue::D8); +} + +TEST_F(VideooutputGeneratedUTest, ColorDepthReturnsInvalidParamsWhenNumericValueIsUnknown) +{ + expectGetterResponse("VideoOutput.colorDepth", nlohmann::json(11)); + + auto result = impl.colorDepth(); + ASSERT_FALSE(result); + EXPECT_EQ(result.error(), Firebolt::Error::InvalidParams); +} + TEST_F(VideooutputGeneratedUTest, ColorDepthForwardsTransportErrors) { expectGetterTransportError("VideoOutput.colorDepth"); @@ -443,3 +479,30 @@ TEST_F(VideooutputGeneratedUTest, SubscribeOnRefreshRateChangedForwardsSubscribe ASSERT_FALSE(result); EXPECT_EQ(result.error(), Firebolt::Error::General); } + +TEST_F(VideooutputGeneratedUTest, SubscribeOnRefreshRateChangedFallsBackToMisspelledAliasWhenNotSupported) +{ + EXPECT_CALL(mockHelper, subscribe(&impl, "VideoOutput.onRefreshRateChanged", ::testing::_, ::testing::_)) + .WillOnce(::testing::Return(Firebolt::Result{Firebolt::Error::CapabilityNotSupported})); + + EXPECT_CALL(mockHelper, subscribe(&impl, "VideoOutput.onRefereshRateChanged", ::testing::_, ::testing::_)) + .WillOnce(::testing::Return(Firebolt::Result{31})); + + auto result = impl.subscribeOnRefreshRateChanged([](const Firebolt::VideoOutput::RefreshRateValue& /*value*/) {}); + + ASSERT_TRUE(result); + EXPECT_EQ(*result, 31U); +} + +TEST_F(VideooutputGeneratedUTest, SubscribeOnRefreshRateChangedDoesNotFallbackOnGeneralError) +{ + EXPECT_CALL(mockHelper, subscribe(&impl, "VideoOutput.onRefreshRateChanged", ::testing::_, ::testing::_)) + .WillOnce(::testing::Return(Firebolt::Result{Firebolt::Error::General})); + + EXPECT_CALL(mockHelper, subscribe(&impl, "VideoOutput.onRefereshRateChanged", ::testing::_, ::testing::_)).Times(0); + + auto result = impl.subscribeOnRefreshRateChanged([](const Firebolt::VideoOutput::RefreshRateValue& /*value*/) {}); + + ASSERT_FALSE(result); + EXPECT_EQ(result.error(), Firebolt::Error::General); +}