From 254b513a07eb84865955600a336f9996c90c085c Mon Sep 17 00:00:00 2001 From: bobra200 Date: Thu, 3 Sep 2026 17:01:07 -0700 Subject: [PATCH 1/8] 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 2/8] 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 3/8] 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 4/8] 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 5/8] 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 6/8] 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 7/8] 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 8/8] 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"));