From 076bc356c03bedb8929d9823e39c26b20fb1e228 Mon Sep 17 00:00:00 2001 From: Zackary Ayoun Date: Wed, 19 Aug 2026 21:24:00 +0100 Subject: [PATCH] NO-NOS: [qsfp_service] Support AOC modules in the qsfp hw test media checks Signed-off-by: Zackary Ayoun --- cmake/QsfpServiceTestHwTest.cmake | 1 + fboss/qsfp_service/test/hw_test/BUCK | 1 + .../test/hw_test/HwI2cSelectTest.cpp | 22 +++++---- .../test/hw_test/HwTransceiverUtils.cpp | 46 +++++++++++++++++++ .../test/hw_test/HwTransceiverUtils.h | 9 ++++ 5 files changed, 69 insertions(+), 10 deletions(-) diff --git a/cmake/QsfpServiceTestHwTest.cmake b/cmake/QsfpServiceTestHwTest.cmake index d570ee644c0c7..676afc667c5a9 100644 --- a/cmake/QsfpServiceTestHwTest.cmake +++ b/cmake/QsfpServiceTestHwTest.cmake @@ -26,6 +26,7 @@ target_link_libraries(hw_transceiver_utils Folly::folly fboss_error platform_mapping + port_test_utils switch_config_cpp2 transceiver_cpp2 transceiver_manager diff --git a/fboss/qsfp_service/test/hw_test/BUCK b/fboss/qsfp_service/test/hw_test/BUCK index 3596b63ae0b8a..33335e2d2cec0 100644 --- a/fboss/qsfp_service/test/hw_test/BUCK +++ b/fboss/qsfp_service/test/hw_test/BUCK @@ -23,6 +23,7 @@ cpp_library( "fbsource//third-party/fmt:fmt", "fbsource//third-party/googletest:gtest", "//fboss/agent:fboss-error", + "//fboss/agent/test/utils:port_test_utils", "//fboss/lib/config:fboss_config_utils", "//fboss/qsfp_service:transceiver-manager", "//fboss/qsfp_service/module/properties:transceiver-properties-manager", diff --git a/fboss/qsfp_service/test/hw_test/HwI2cSelectTest.cpp b/fboss/qsfp_service/test/hw_test/HwI2cSelectTest.cpp index 144db1a0c4a3d..b7ecdb1a3a49c 100644 --- a/fboss/qsfp_service/test/hw_test/HwI2cSelectTest.cpp +++ b/fboss/qsfp_service/test/hw_test/HwI2cSelectTest.cpp @@ -4,8 +4,8 @@ #include "fboss/qsfp_service/test/hw_test/HwPortUtils.h" #include "fboss/qsfp_service/test/hw_test/HwQsfpEnsemble.h" +#include "fboss/qsfp_service/test/hw_test/HwTransceiverUtils.h" -#include #include namespace facebook::fboss { @@ -68,21 +68,23 @@ TEST_F(HwTest, i2cUniqueSerialNumbers) { if (snIds.find(sn) == snIds.end()) { snIds[sn] = tcvrId; } else { - // Found duplicated serial numbers, module must be DAC and connected to - // another DAC on the other end + // Found duplicated serial numbers, module must be a single cable + // assembly (DAC/AEC/AOC) connected back to itself on the other end auto neighborId = snIds[sn]; CHECK(cabledNames.find(tcvrId) != cabledNames.end()); CHECK(cabledNames.find(neighborId) != cabledNames.end()); EXPECT_EQ(cabledNames[tcvrId].first, cabledNames[neighborId].second); EXPECT_EQ(cabledNames[tcvrId].second, cabledNames[neighborId].first); - auto transmitterTech = - *(transceiverInfo[tcvrId].tcvrState()->cable()->transmitterTech()); - EXPECT_EQ(TransmitterTechnology::COPPER, transmitterTech); - - transmitterTech = *( - transceiverInfo[neighborId].tcvrState()->cable()->transmitterTech()); - EXPECT_EQ(TransmitterTechnology::COPPER, transmitterTech); + for (auto id : {tcvrId, neighborId}) { + const auto& tcvrState = *transceiverInfo[id].tcvrState(); + auto transmitterTech = *(tcvrState.cable()->transmitterTech()); + EXPECT_TRUE( + transmitterTech == TransmitterTechnology::COPPER || + utility::HwTransceiverUtils::isAoc(tcvrState)) + << "Transceiver " << id + << " shares a serial number but is neither copper nor AOC"; + } } } } diff --git a/fboss/qsfp_service/test/hw_test/HwTransceiverUtils.cpp b/fboss/qsfp_service/test/hw_test/HwTransceiverUtils.cpp index fbf5f5391ab1d..4d27661866c9c 100644 --- a/fboss/qsfp_service/test/hw_test/HwTransceiverUtils.cpp +++ b/fboss/qsfp_service/test/hw_test/HwTransceiverUtils.cpp @@ -9,6 +9,7 @@ */ #include "fboss/qsfp_service/test/hw_test/HwTransceiverUtils.h" +#include #include #include @@ -18,8 +19,10 @@ #include "fboss/agent/FbossError.h" #include "fboss/agent/gen-cpp2/switch_config_types.h" #include "fboss/agent/platforms/common/PlatformMapping.h" +#include "fboss/agent/test/utils/PortTestUtils.h" #include "fboss/lib/config/PlatformConfigUtils.h" #include "fboss/qsfp_service/TransceiverManager.h" +#include "fboss/qsfp_service/module/cmis/CmisHelper.h" #include "fboss/qsfp_service/module/properties/TransceiverPropertiesManager.h" namespace facebook::fboss::utility { @@ -354,6 +357,14 @@ void HwTransceiverUtils::verifyMediaInterfaceCompliance( mediaInterfaces.push_back(allMediaInterfaces[mediaLane]); } + if (isAoc(tcvrState)) { + // An AOC programs like an active cable regardless of the profile's + // copper/optical suffix, so the per-profile verifiers below (which + // read smfCode or expect a copper transmitter) don't apply. + verifyActiveOpticalCableProfile(mgmtInterface, mediaInterfaces, profile); + return; + } + switch (profile) { case cfg::PortProfileID::PROFILE_10G_1_NRZ_NOFEC_OPTICAL: verify10gProfile(tcvrState, mgmtInterface, mediaInterfaces); @@ -651,6 +662,41 @@ void HwTransceiverUtils::verifyCopper800gProfile( } } +bool HwTransceiverUtils::isAoc(const TcvrState& tcvrState) { + return TransceiverManager::activeCable(tcvrState) && + *tcvrState.cable()->transmitterTech() == TransmitterTechnology::OPTICAL; +} + +void HwTransceiverUtils::verifyActiveOpticalCableProfile( + const TransceiverManagementInterface mgmtInterface, + const std::vector& mediaInterfaces, + cfg::PortProfileID profile) { + EXPECT_EQ(mgmtInterface, TransceiverManagementInterface::CMIS); + + const auto& speedApplications = CmisHelper::getActiveSpeedApplication(); + auto expectedCodesIt = speedApplications.find(utility::getSpeed(profile)); + if (expectedCodesIt == speedApplications.end()) { + throw FbossError( + "No active cable application for profile ", + apache::thrift::util::enumNameSafe(profile)); + } + const auto& expectedCodes = expectedCodesIt->second; + const auto& activeMediaMap = CmisHelper::getActiveMediaInterfaceMapping(); + + for (const auto& mediaId : mediaInterfaces) { + auto activeCuCode = *mediaId.media()->activeCuCode(); + EXPECT_TRUE( + std::find(expectedCodes.begin(), expectedCodes.end(), activeCuCode) != + expectedCodes.end()) + << "Unexpected activeCuCode " + << apache::thrift::util::enumNameSafe(activeCuCode) << " for profile " + << apache::thrift::util::enumNameSafe(profile); + auto mediaCodeIt = activeMediaMap.find(activeCuCode); + ASSERT_TRUE(mediaCodeIt != activeMediaMap.end()); + EXPECT_EQ(*mediaId.code(), mediaCodeIt->second); + } +} + void HwTransceiverUtils::verifyDataPathEnabled( const TcvrState& tcvrState, const std::string& portName) { diff --git a/fboss/qsfp_service/test/hw_test/HwTransceiverUtils.h b/fboss/qsfp_service/test/hw_test/HwTransceiverUtils.h index 76ff5a1fcc505..ed17a57a1e8d2 100644 --- a/fboss/qsfp_service/test/hw_test/HwTransceiverUtils.h +++ b/fboss/qsfp_service/test/hw_test/HwTransceiverUtils.h @@ -50,7 +50,16 @@ class HwTransceiverUtils { time_t timeReference, bool expectedReset); + // Active optical cable: reports ACTIVE_CABLES media type encoding like an + // AEC, but an optical transmitter. Its media interface union carries + // activeCuCode, not smfCode. + static bool isAoc(const TcvrState& tcvrState); + private: + static void verifyActiveOpticalCableProfile( + TransceiverManagementInterface mgmtInterface, + const std::vector& mediaInterfaces, + cfg::PortProfileID profile); static void verifyOpticsSettings( const TcvrState& tcvrState, const std::string& portName,