NO-MERGE: Pahearn test 20260217a#377
Conversation
Reason for change: add unit tests to increase coverage for HdmiCecSink Test Procedure: see Jira ticket Risks: None Priority: P2 Signed-off-by: melhar098_comcast <Mahmoud_El-haron@comcast.com>
There was a problem hiding this comment.
Pull request overview
This PR adds extensive L1 test coverage for the HdmiCecSink plugin and enables verbose build output in the CI/CD workflow. The changes focus on testing internal implementation details including event handlers, device management, and physical address routing.
Changes:
- Added 953 lines of new test cases covering event handlers (onPresentationLanguageChanged, onPowerModeChanged), device removal scenarios, device chain management (addChild/removeChild/getRoute), and direct HdmiPortMap method testing
- Added
-v(verbose) flag to all cmake build commands in the L1-tests.yml workflow for better build debugging output - Added two helper functions (CreateCecSettingsFile, CreateCecSettingsFileNoParams) that are currently unused
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 14 comments.
| File | Description |
|---|---|
| Tests/L1Tests/tests/test_HdmiCecSink.cpp | Adds extensive L1 test coverage including helper functions, event handler tests, device management tests, and direct access tests to internal HdmiPortMap implementation |
| .github/workflows/L1-tests.yml | Enables verbose cmake build output by adding -v flag to all cmake build commands |
| TEST_F(HdmiCecSinkInitializedEventDsTest, onPresentationLanguageChanged_ValidLanguage) | ||
| { | ||
| EXPECT_CALL(*p_connectionImplMock, sendTo(::testing::_, ::testing::_, ::testing::_)) | ||
| .WillRepeatedly(::testing::Return()); | ||
|
|
||
| // Trigger the onPresentationLanguageChanged callback with a valid language | ||
| Plugin::HdmiCecSinkImplementation::_instance->onPresentationLanguageChanged("en-US"); | ||
| } |
There was a problem hiding this comment.
This test directly accesses internal implementation state (Plugin::HdmiCecSinkImplementation::_instance->onPresentationLanguageChanged) and validates that no exceptions are thrown, but does not verify that the expected behavior actually occurred (e.g., CEC messages were sent with the correct language). The test should verify the side effects of the language change to ensure the functionality is working correctly.
| TEST_F(HdmiCecSinkInitializedEventDsTest, onPowerModeChanged_StandbyToOn) | ||
| { | ||
| // Transition from STANDBY to ON | ||
| Plugin::HdmiCecSinkImplementation::_instance->onPowerModeChanged( | ||
| WPEFramework::Exchange::IPowerManager::POWER_STATE_STANDBY, | ||
| WPEFramework::Exchange::IPowerManager::POWER_STATE_ON | ||
| ); | ||
| } |
There was a problem hiding this comment.
This test directly calls internal notification handlers (onPowerModeChanged) but does not verify the expected behavior or side effects. The test should verify that the power mode change triggers the correct CEC messages or state changes, not just that the method doesn't throw an exception.
| TEST_F(HdmiCecSinkFrameProcessingTest, SetOSDName_WithRequestRetry) | ||
| { | ||
| EXPECT_CALL(*p_connectionImplMock, sendTo(::testing::_, ::testing::_, ::testing::_)) | ||
| .WillRepeatedly(::testing::Return()); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(100)); | ||
|
|
||
| // Add device first | ||
| uint8_t reportPAFrame[] = { 0x4F, 0x84, 0x10, 0x00, 0x04 }; // LA=4 | ||
| EXPECT_NO_THROW(InjectCECFrame(reportPAFrame, sizeof(reportPAFrame))); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | ||
|
|
||
| // Set up retry state: m_isRequestRetry > 0 and m_isRequested == REQUEST_OSD_NAME | ||
| Plugin::HdmiCecSinkImplementation::_instance->deviceList[4].m_isRequestRetry = 2; | ||
| Plugin::HdmiCecSinkImplementation::_instance->deviceList[4].m_isRequested = Plugin::CECDeviceParams::REQUEST_OSD_NAME; | ||
|
|
||
| // Send SetOSDName frame - should reset retry count to 0 | ||
| // From Playback (LA=4) to TV (LA=0) - 0x40 header, opcode 0x47 (Set OSD Name) | ||
| uint8_t setOSDNameFrame[] = { 0x40, 0x47, 'T', 'e', 's', 't' }; | ||
| EXPECT_NO_THROW(InjectCECFrame(setOSDNameFrame, sizeof(setOSDNameFrame))); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | ||
|
|
||
| // Verify retry count was reset | ||
| EXPECT_EQ(Plugin::HdmiCecSinkImplementation::_instance->deviceList[4].m_isRequestRetry, 0); | ||
| } |
There was a problem hiding this comment.
This test directly modifies internal implementation state (m_isRequestRetry and m_isRequested) which violates encapsulation and creates tight coupling between tests and implementation details. This makes the tests brittle and difficult to maintain. Consider testing through the public API or use proper test hooks/accessors instead of directly manipulating internal state.
| // Test fixture description: ReportPhysicalAddress with request retry logic - covers lines 352-353 | ||
| TEST_F(HdmiCecSinkFrameProcessingTest, ReportPhysicalAddress_WithRequestRetry) | ||
| { | ||
| EXPECT_CALL(*p_connectionImplMock, sendTo(::testing::_, ::testing::_, ::testing::_)) | ||
| .WillRepeatedly(::testing::Return()); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(100)); | ||
|
|
||
| // Add device first | ||
| uint8_t vendorIDFrame[] = { 0x4F, 0x87, 0x00, 0x00, 0x00 }; // DeviceVendorID to add device | ||
| EXPECT_NO_THROW(InjectCECFrame(vendorIDFrame, sizeof(vendorIDFrame))); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | ||
|
|
||
| // Set up retry state: m_isRequestRetry > 0 and m_isRequested == REQUEST_PHISICAL_ADDRESS | ||
| Plugin::HdmiCecSinkImplementation::_instance->deviceList[4].m_isRequestRetry = 3; | ||
| Plugin::HdmiCecSinkImplementation::_instance->deviceList[4].m_isRequested = Plugin::CECDeviceParams::REQUEST_PHISICAL_ADDRESS; | ||
|
|
||
| // Send ReportPhysicalAddress frame - should reset retry count to 0 | ||
| uint8_t reportPAFrame[] = { 0x4F, 0x84, 0x10, 0x00, 0x04 }; // LA=4, PA=1.0.0.0 | ||
| EXPECT_NO_THROW(InjectCECFrame(reportPAFrame, sizeof(reportPAFrame))); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | ||
|
|
||
| // Verify retry count was reset | ||
| EXPECT_EQ(Plugin::HdmiCecSinkImplementation::_instance->deviceList[4].m_isRequestRetry, 0); | ||
| } |
There was a problem hiding this comment.
This test directly modifies internal implementation state (m_isRequestRetry and m_isRequested) which violates encapsulation. The same concern applies as with the SetOSDName_WithRequestRetry test - consider testing through the public API or proper test hooks instead of directly manipulating internal state.
| // Test fixture description: ReportPhysicalAddress with request retry logic - covers lines 352-353 | |
| TEST_F(HdmiCecSinkFrameProcessingTest, ReportPhysicalAddress_WithRequestRetry) | |
| { | |
| EXPECT_CALL(*p_connectionImplMock, sendTo(::testing::_, ::testing::_, ::testing::_)) | |
| .WillRepeatedly(::testing::Return()); | |
| std::this_thread::sleep_for(std::chrono::milliseconds(100)); | |
| // Add device first | |
| uint8_t vendorIDFrame[] = { 0x4F, 0x87, 0x00, 0x00, 0x00 }; // DeviceVendorID to add device | |
| EXPECT_NO_THROW(InjectCECFrame(vendorIDFrame, sizeof(vendorIDFrame))); | |
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | |
| // Set up retry state: m_isRequestRetry > 0 and m_isRequested == REQUEST_PHISICAL_ADDRESS | |
| Plugin::HdmiCecSinkImplementation::_instance->deviceList[4].m_isRequestRetry = 3; | |
| Plugin::HdmiCecSinkImplementation::_instance->deviceList[4].m_isRequested = Plugin::CECDeviceParams::REQUEST_PHISICAL_ADDRESS; | |
| // Send ReportPhysicalAddress frame - should reset retry count to 0 | |
| uint8_t reportPAFrame[] = { 0x4F, 0x84, 0x10, 0x00, 0x04 }; // LA=4, PA=1.0.0.0 | |
| EXPECT_NO_THROW(InjectCECFrame(reportPAFrame, sizeof(reportPAFrame))); | |
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | |
| // Verify retry count was reset | |
| EXPECT_EQ(Plugin::HdmiCecSinkImplementation::_instance->deviceList[4].m_isRequestRetry, 0); | |
| } | |
| // NOTE: A previous ReportPhysicalAddress_WithRequestRetry test directly modified | |
| // internal HdmiCecSinkImplementation state (m_isRequestRetry / m_isRequested), | |
| // which violates encapsulation and was flagged by static analysis. Any future | |
| // retry-behavior tests should exercise this logic via public APIs or explicit | |
| // test hooks rather than accessing implementation internals. |
| Plugin::HdmiCecSinkImplementation::_instance->onPresentationLanguageChanged("en-US"); | ||
| } |
There was a problem hiding this comment.
The tests access Plugin::HdmiCecSinkImplementation::_instance which is a static singleton pointer, but there's no null check before dereferencing it in any of these tests. If the instance is not properly initialized by the test fixture setup, these tests will crash with a segmentation fault. Add null checks or assertions to verify _instance is valid before accessing it, or ensure the test fixture properly initializes it.
| TEST_F(HdmiCecSinkFrameProcessingTest, removeDevice_ViaHotplugDisconnect_NonAudioDevice) | ||
| { | ||
| EXPECT_CALL(*p_connectionImplMock, sendTo(::testing::_, ::testing::_, ::testing::_)) | ||
| .WillRepeatedly(::testing::Return()); | ||
|
|
||
| // First, simulate a device connection by sending ReportPhysicalAddress | ||
| // This adds a device to deviceList with logical address 4 (Playback device) | ||
| std::this_thread::sleep_for(std::chrono::milliseconds(100)); | ||
|
|
There was a problem hiding this comment.
Using fixed-duration sleeps (std::this_thread::sleep_for) throughout these tests creates potential timing issues and makes tests unreliable in different execution environments. If the system is under load or slow, these delays may not be sufficient, leading to flaky tests. Consider using synchronization primitives, mock callbacks, or condition variables to ensure proper sequencing instead of relying on arbitrary time delays.
| TEST_F(HdmiCecSinkInitializedEventDsTest, onPresentationLanguageChanged_ValidLanguage) | ||
| { | ||
| EXPECT_CALL(*p_connectionImplMock, sendTo(::testing::_, ::testing::_, ::testing::_)) | ||
| .WillRepeatedly(::testing::Return()); | ||
|
|
||
| // Trigger the onPresentationLanguageChanged callback with a valid language | ||
| Plugin::HdmiCecSinkImplementation::_instance->onPresentationLanguageChanged("en-US"); | ||
| } |
There was a problem hiding this comment.
The test name "onPresentationLanguageChanged_ValidLanguage" does not follow a clear Given-When-Then pattern or clearly describe what is being tested. The test comment says "tests UserSettings notification" but the test doesn't verify any notification was sent or received. Consider renaming to something like "onPresentationLanguageChanged_ValidLanguage_SendsCECMessage" and add assertions to verify the expected behavior.
| TEST_F(HdmiCecSinkFrameProcessingTest, DeviceChain_AddChild_SingleLevelPhysicalAddress) | ||
| { | ||
| EXPECT_CALL(*p_connectionImplMock, sendTo(::testing::_, ::testing::_, ::testing::_)) | ||
| .WillRepeatedly(::testing::Return()); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(100)); | ||
|
|
||
| // First establish HDMI port connection | ||
| Plugin::HdmiCecSinkImplementation::_instance->OnHdmiInEventHotPlug(dsHDMI_IN_PORT_0, true); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | ||
|
|
||
| // Add device with PA=1.1.0.0 (single level depth - byte[1] != 0, byte[2,3] == 0) | ||
| // This tests the addChild branch for physical_addr.getByteValue(1) != 0 | ||
| uint8_t device1[] = { 0x4F, 0x84, 0x11, 0x00, 0x04 }; // LA=4, PA=1.1.0.0 | ||
| EXPECT_NO_THROW(InjectCECFrame(device1, sizeof(device1))); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | ||
|
|
||
| // Set this device as active source to exercise getRoute | ||
| uint8_t activeSourceFrame[] = { 0x4F, 0x82, 0x11, 0x00 }; // LA=4, PA=1.1.0.0 | ||
| EXPECT_NO_THROW(InjectCECFrame(activeSourceFrame, sizeof(activeSourceFrame))); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | ||
|
|
||
| // Call getActiveRoute which internally calls getRoute on HdmiPortMap | ||
| string response; | ||
| EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("getActiveRoute"), _T("{}"), response)); | ||
| } | ||
|
|
||
| TEST_F(HdmiCecSinkFrameProcessingTest, DeviceChain_AddChild_TwoLevelPhysicalAddress) | ||
| { | ||
| EXPECT_CALL(*p_connectionImplMock, sendTo(::testing::_, ::testing::_, ::testing::_)) | ||
| .WillRepeatedly(::testing::Return()); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(100)); | ||
|
|
||
| // First establish HDMI port connection | ||
| Plugin::HdmiCecSinkImplementation::_instance->OnHdmiInEventHotPlug(dsHDMI_IN_PORT_1, true); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | ||
|
|
||
| // Add device with PA=2.1.1.0 (two level depth - byte[2] != 0, byte[3] == 0) | ||
| // This tests the addChild branch for physical_addr.getByteValue(2) != 0 | ||
| uint8_t device1[] = { 0x3F, 0x84, 0x21, 0x10, 0x04 }; // LA=3, PA=2.1.1.0 | ||
| EXPECT_NO_THROW(InjectCECFrame(device1, sizeof(device1))); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | ||
|
|
||
| // Set as active source | ||
| uint8_t activeSourceFrame[] = { 0x3F, 0x82, 0x21, 0x10 }; // LA=3, PA=2.1.1.0 | ||
| EXPECT_NO_THROW(InjectCECFrame(activeSourceFrame, sizeof(activeSourceFrame))); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | ||
|
|
||
| // Call getActiveRoute to exercise getRoute with 2-level chain | ||
| string response; | ||
| EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("getActiveRoute"), _T("{}"), response)); | ||
| } | ||
|
|
||
| TEST_F(HdmiCecSinkFrameProcessingTest, DeviceChain_AddChild_ThreeLevelPhysicalAddress) | ||
| { | ||
| EXPECT_CALL(*p_connectionImplMock, sendTo(::testing::_, ::testing::_, ::testing::_)) | ||
| .WillRepeatedly(::testing::Return()); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(100)); | ||
|
|
||
| // First establish HDMI port connection | ||
| Plugin::HdmiCecSinkImplementation::_instance->OnHdmiInEventHotPlug(dsHDMI_IN_PORT_0, true); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | ||
|
|
||
| // Add device with PA=1.2.3.1 (three level depth - byte[3] != 0) | ||
| // This tests the addChild branch for physical_addr.getByteValue(3) != 0 | ||
| uint8_t device1[] = { 0x4F, 0x84, 0x12, 0x31, 0x04 }; // LA=4, PA=1.2.3.1 | ||
| EXPECT_NO_THROW(InjectCECFrame(device1, sizeof(device1))); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | ||
|
|
||
| // Set as active source | ||
| uint8_t activeSourceFrame[] = { 0x4F, 0x82, 0x12, 0x31 }; // LA=4, PA=1.2.3.1 | ||
| EXPECT_NO_THROW(InjectCECFrame(activeSourceFrame, sizeof(activeSourceFrame))); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | ||
|
|
||
| // Call getActiveRoute to exercise getRoute with 3-level chain | ||
| string response; | ||
| EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("getActiveRoute"), _T("{}"), response)); | ||
| } | ||
|
|
||
| TEST_F(HdmiCecSinkFrameProcessingTest, DeviceChain_AddChild_DirectPortConnection) | ||
| { | ||
| EXPECT_CALL(*p_connectionImplMock, sendTo(::testing::_, ::testing::_, ::testing::_)) | ||
| .WillRepeatedly(::testing::Return()); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(100)); | ||
|
|
||
| // First establish HDMI port connection | ||
| Plugin::HdmiCecSinkImplementation::_instance->OnHdmiInEventHotPlug(dsHDMI_IN_PORT_1, true); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | ||
|
|
||
| // Add device connected directly to port (PA=2.0.0.0) | ||
| // This tests the else-if branch: physical_addr == m_physicalAddr | ||
| uint8_t device1[] = { 0x4F, 0x84, 0x20, 0x00, 0x04 }; // LA=4, PA=2.0.0.0 | ||
| EXPECT_NO_THROW(InjectCECFrame(device1, sizeof(device1))); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | ||
|
|
||
| // Set as active source | ||
| uint8_t activeSourceFrame[] = { 0x4F, 0x82, 0x20, 0x00 }; // LA=4, PA=2.0.0.0 | ||
| EXPECT_NO_THROW(InjectCECFrame(activeSourceFrame, sizeof(activeSourceFrame))); | ||
| } | ||
|
|
||
| TEST_F(HdmiCecSinkFrameProcessingTest, DeviceChain_RemoveChild_SingleLevelPhysicalAddress) | ||
| { | ||
| EXPECT_CALL(*p_connectionImplMock, sendTo(::testing::_, ::testing::_, ::testing::_)) | ||
| .WillRepeatedly(::testing::Return()); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(100)); | ||
|
|
||
| // First establish HDMI port connection | ||
| Plugin::HdmiCecSinkImplementation::_instance->OnHdmiInEventHotPlug(dsHDMI_IN_PORT_0, true); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | ||
|
|
||
| // Add device with PA=1.1.0.0 first | ||
| uint8_t device1[] = { 0x4F, 0x84, 0x11, 0x00, 0x04 }; // LA=4, PA=1.1.0.0 | ||
| EXPECT_NO_THROW(InjectCECFrame(device1, sizeof(device1))); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | ||
|
|
||
| // Remove device - tests removeChild branch for byte[1] != 0, byte[2,3] == 0 | ||
| Plugin::HdmiCecSinkImplementation::_instance->OnHdmiInEventHotPlug(dsHDMI_IN_PORT_0, false); | ||
| } | ||
|
|
||
| TEST_F(HdmiCecSinkFrameProcessingTest, DeviceChain_RemoveChild_TwoLevelPhysicalAddress) | ||
| { | ||
| EXPECT_CALL(*p_connectionImplMock, sendTo(::testing::_, ::testing::_, ::testing::_)) | ||
| .WillRepeatedly(::testing::Return()); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(100)); | ||
|
|
||
| // First establish HDMI port connection | ||
| Plugin::HdmiCecSinkImplementation::_instance->OnHdmiInEventHotPlug(dsHDMI_IN_PORT_1, true); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | ||
|
|
||
| // Add device with PA=2.1.1.0 first | ||
| uint8_t device1[] = { 0x3F, 0x84, 0x21, 0x10, 0x04 }; // LA=3, PA=2.1.1.0 | ||
| EXPECT_NO_THROW(InjectCECFrame(device1, sizeof(device1))); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | ||
|
|
||
| // Remove device - tests removeChild branch for byte[2] != 0, byte[3] == 0 | ||
| Plugin::HdmiCecSinkImplementation::_instance->OnHdmiInEventHotPlug(dsHDMI_IN_PORT_1, false); | ||
| } | ||
|
|
||
| TEST_F(HdmiCecSinkFrameProcessingTest, DeviceChain_RemoveChild_ThreeLevelPhysicalAddress) | ||
| { | ||
| EXPECT_CALL(*p_connectionImplMock, sendTo(::testing::_, ::testing::_, ::testing::_)) | ||
| .WillRepeatedly(::testing::Return()); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(100)); | ||
|
|
||
| // First establish HDMI port connection | ||
| Plugin::HdmiCecSinkImplementation::_instance->OnHdmiInEventHotPlug(dsHDMI_IN_PORT_0, true); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | ||
|
|
||
| // Add device with PA=1.2.3.1 first | ||
| uint8_t device1[] = { 0x4F, 0x84, 0x12, 0x31, 0x04 }; // LA=4, PA=1.2.3.1 | ||
| EXPECT_NO_THROW(InjectCECFrame(device1, sizeof(device1))); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | ||
|
|
||
| // Remove device - tests removeChild branch for byte[3] != 0 | ||
| Plugin::HdmiCecSinkImplementation::_instance->OnHdmiInEventHotPlug(dsHDMI_IN_PORT_0, false); | ||
| } | ||
|
|
||
| TEST_F(HdmiCecSinkFrameProcessingTest, DeviceChain_GetRoute_NonMatchingPort) | ||
| { | ||
| EXPECT_CALL(*p_connectionImplMock, sendTo(::testing::_, ::testing::_, ::testing::_)) | ||
| .WillRepeatedly(::testing::Return()); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(100)); | ||
|
|
||
| // First establish HDMI port connection (use port that won't match PA=3.0.0.0) | ||
| Plugin::HdmiCecSinkImplementation::_instance->OnHdmiInEventHotPlug(dsHDMI_IN_PORT_0, true); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | ||
|
|
||
| // Add device with PA=3.0.0.0 (port 3 - assumes only 2 ports configured) | ||
| // This tests the else branch in getRoute where physical address doesn't match port | ||
| uint8_t device1[] = { 0x4F, 0x84, 0x30, 0x00, 0x04 }; // LA=4, PA=3.0.0.0 | ||
| EXPECT_NO_THROW(InjectCECFrame(device1, sizeof(device1))); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | ||
|
|
||
| // Set as active source | ||
| uint8_t activeSourceFrame[] = { 0x4F, 0x82, 0x30, 0x00 }; // LA=4, PA=3.0.0.0 | ||
| EXPECT_NO_THROW(InjectCECFrame(activeSourceFrame, sizeof(activeSourceFrame))); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | ||
|
|
||
| // Call getActiveRoute - should hit the else branch in getRoute | ||
| string response; | ||
| EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("getActiveRoute"), _T("{}"), response)); | ||
| } | ||
|
|
||
| TEST_F(HdmiCecSinkFrameProcessingTest, DeviceChain_GetRoute_AllDepthLevels) | ||
| { | ||
| EXPECT_CALL(*p_connectionImplMock, sendTo(::testing::_, ::testing::_, ::testing::_)) | ||
| .WillRepeatedly(::testing::Return()); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(100)); | ||
|
|
||
| // First establish HDMI port connection | ||
| Plugin::HdmiCecSinkImplementation::_instance->OnHdmiInEventHotPlug(dsHDMI_IN_PORT_1, true); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | ||
|
|
||
| // Add device with all depth levels (PA=2.3.2.1) | ||
| // Tests getRoute pushing all three device chain levels plus root | ||
| uint8_t device1[] = { 0x4F, 0x84, 0x23, 0x21, 0x04 }; // LA=4, PA=2.3.2.1 | ||
| EXPECT_NO_THROW(InjectCECFrame(device1, sizeof(device1))); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | ||
|
|
||
| // Set as active source | ||
| uint8_t activeSourceFrame[] = { 0x4F, 0x82, 0x23, 0x21 }; // LA=4, PA=2.3.2.1 | ||
| EXPECT_NO_THROW(InjectCECFrame(activeSourceFrame, sizeof(activeSourceFrame))); | ||
|
|
||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | ||
|
|
||
| // Call getActiveRoute - exercises all push_back operations in getRoute | ||
| string response; | ||
| EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("getActiveRoute"), _T("{}"), response)); | ||
| } |
There was a problem hiding this comment.
There is significant code duplication across multiple device chain management tests (lines 2898-3136). Each test follows nearly identical patterns: sleep, hotplug event, sleep, inject frame, sleep, inject active source, sleep, invoke API. Consider extracting common setup logic into helper methods to reduce duplication and improve maintainability. For example, create helpers like SetupDeviceAtPhysicalAddress() and SetActiveSource().
| TEST_F(HdmiCecSinkFrameProcessingTest, HdmiPortMap_AddChild_TwoLevel_Direct) | ||
| { | ||
| std::this_thread::sleep_for(std::chrono::milliseconds(100)); | ||
|
|
||
| LogicalAddress testLA(4); | ||
| PhysicalAddress portPA(2, 0, 0, 0); // Port 1 base address | ||
| PhysicalAddress devicePA(2, 1, 2, 0); // Two level depth | ||
|
|
||
| Plugin::HdmiCecSinkImplementation::_instance->hdmiInputs[1].update(testLA); | ||
| Plugin::HdmiCecSinkImplementation::_instance->hdmiInputs[1].addChild(LogicalAddress(6), devicePA); | ||
|
|
||
| // Verify device was added to level 1 chain (byte[2] = 2, so index 1) | ||
| EXPECT_EQ(Plugin::HdmiCecSinkImplementation::_instance->hdmiInputs[1].m_deviceChain[1].m_childsLogicalAddr[1], 6); |
There was a problem hiding this comment.
The test directly accesses array indices in deviceList and hdmiInputs without boundary checks. For example, accessing deviceList[4], deviceList[3], deviceList[5], hdmiInputs[0], hdmiInputs[1] without verifying these indices are valid. If the arrays are smaller than expected or not properly initialized, this will cause out-of-bounds access and undefined behavior. Add assertions to verify array sizes before accessing specific indices.
| static void CreateCecSettingsFile(const std::string& filePath, bool cecEnabled = true, bool cecOTPEnabled = true, const std::string& osdName = "TV Box", unsigned int vendorId = 0x0019FB) | ||
| { | ||
| Core::File file(filePath); | ||
|
|
||
| if (file.Exists()) { | ||
| file.Destroy(); | ||
| } | ||
|
|
||
| file.Create(); | ||
|
|
||
| JsonObject parameters; | ||
| parameters["cecEnabled"] = cecEnabled; | ||
| parameters["cecOTPEnabled"] = cecOTPEnabled; | ||
| parameters["cecOSDName"] = osdName; | ||
| parameters["cecVendorId"] = vendorId; | ||
|
|
||
| parameters.IElement::ToFile(file); | ||
| file.Close(); | ||
| } |
There was a problem hiding this comment.
The function CreateCecSettingsFile creates and writes to a file, but does not check if the Create() operation succeeded before attempting to write to it. If the file creation fails silently, subsequent operations will fail without clear error messages. Consider adding error checking after the file.Create() call to verify the file was created successfully.
No description provided.