From a67733c6714339ef67cefdd5697ba9ef7334739c Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Sat, 31 Oct 2026 19:20:53 -0400 Subject: [PATCH] add some lifecycle tests for telephone interconnect; --- src/fne/network/callhandler/TagP25Data.h | 11 ++++++++ tests/fne/FNETestHooks.cpp | 29 +++++++++++++++++++++ tests/fne/FNETestHooks.h | 29 +++++++++++++++++++++ tests/p25/HostControl_Tests.cpp | 33 ++++++++++++++++++++++++ 4 files changed, 102 insertions(+) diff --git a/src/fne/network/callhandler/TagP25Data.h b/src/fne/network/callhandler/TagP25Data.h index edd59118..aaa1a0e2 100644 --- a/src/fne/network/callhandler/TagP25Data.h +++ b/src/fne/network/callhandler/TagP25Data.h @@ -32,6 +32,14 @@ #include "network/TrafficNetwork.h" #include "network/callhandler/packetdata/P25PacketData.h" +// --------------------------------------------------------------------------- +// Class Prototypes +// --------------------------------------------------------------------------- + +#if defined(CATCH2_TEST_COMPILATION) +class FNETestHooks; +#endif + namespace network { namespace callhandler @@ -171,6 +179,9 @@ namespace network packetdata::P25PacketData* packetData() { return m_packetData; } private: +#if defined(CATCH2_TEST_COMPILATION) + friend class ::FNETestHooks; +#endif TrafficNetwork* m_network; /** diff --git a/tests/fne/FNETestHooks.cpp b/tests/fne/FNETestHooks.cpp index 77dd2682..eda40046 100644 --- a/tests/fne/FNETestHooks.cpp +++ b/tests/fne/FNETestHooks.cpp @@ -10,6 +10,8 @@ #include "fne/FNETestHooks.h" #include "fne/HostFNE.h" #include "fne/network/P25OTARService.h" +#include "fne/network/callhandler/TagP25Data.h" +#include "fne/lookups/AffiliationLookup.h" #include "common/p25/data/Assembler.h" #include "common/Utils.h" @@ -31,6 +33,33 @@ bool g_promiscuousHub = false; // Public Class Members // --------------------------------------------------------------------------- +/* Tests whether a P25 frame is telephone interconnect traffic. */ + +bool FNETestHooks::isP25TelIntTraffic(TrafficNetwork& network, p25::lc::LC& control, + DUID::E duid, const p25::lc::TSBK* tsbk) +{ + return network.m_tagP25->isTelIntTraffic(control, duid, tsbk); +} + +/* Tests whether telephone interconnect traffic may be routed between two peers. */ + +bool FNETestHooks::isP25TelIntPeerPermitted(TrafficNetwork& network, uint32_t srcPeerId, + uint32_t dstPeerId, uint32_t dstId) +{ + return network.m_tagP25->isTelIntPeerPermitted(srcPeerId, dstPeerId, + network.findPeer(dstPeerId), dstId); +} + +/* Adds a unit registration owned by a site peer. */ + +void FNETestHooks::registerP25Unit(TrafficNetwork& network, uint32_t sitePeerId, uint32_t unitId) +{ + std::shared_ptr affiliations = + std::make_shared("test-p25-registrations", nullptr, false); + affiliations->unitReg(unitId, sitePeerId); + network.m_peerAffiliations[sitePeerId] = affiliations; +} + /** * @brief Initializes an instance of the HostFNE class. */ diff --git a/tests/fne/FNETestHooks.h b/tests/fne/FNETestHooks.h index fa394e6f..a770ccdc 100644 --- a/tests/fne/FNETestHooks.h +++ b/tests/fne/FNETestHooks.h @@ -12,6 +12,8 @@ #include "fne/network/MetadataNetwork.h" #include "fne/network/TrafficNetwork.h" +#include "common/p25/lc/LC.h" +#include "common/p25/lc/TSBK.h" #include #include @@ -28,6 +30,33 @@ */ class FNETestHooks { public: + /** + * @brief Tests whether a P25 frame is classified as telephone interconnect traffic. + * @param network The TrafficNetwork instance. + * @param control Link control associated with the frame. + * @param duid Data unit identifier. + * @param tsbk Optional trunking signalling block for a TSDU. + * @return True when the frame is telephone interconnect traffic. + */ + static bool isP25TelIntTraffic(network::TrafficNetwork& network, p25::lc::LC& control, + p25::defines::DUID::E duid, const p25::lc::TSBK* tsbk = nullptr); + /** + * @brief Tests whether telephone interconnect traffic may be routed between two peers. + * @param network The TrafficNetwork instance. + * @param srcPeerId Originating peer ID. + * @param dstPeerId Destination peer ID. + * @param dstId Subscriber radio ID carried by the call. + * @return True when routing to the destination peer is permitted. + */ + static bool isP25TelIntPeerPermitted(network::TrafficNetwork& network, uint32_t srcPeerId, + uint32_t dstPeerId, uint32_t dstId); + /** + * @brief Adds a unit registration owned by a site peer. + * @param network The TrafficNetwork instance. + * @param sitePeerId Site peer that owns the registration. + * @param unitId Registered subscriber radio ID. + */ + static void registerP25Unit(network::TrafficNetwork& network, uint32_t sitePeerId, uint32_t unitId); /** * @brief Adds a peer to the specified TrafficNetwork. * @param network The TrafficNetwork instance. diff --git a/tests/p25/HostControl_Tests.cpp b/tests/p25/HostControl_Tests.cpp index d08a8ad1..291bca27 100644 --- a/tests/p25/HostControl_Tests.cpp +++ b/tests/p25/HostControl_Tests.cpp @@ -1075,6 +1075,39 @@ TEST_CASE("P25 non-authoritative unpermit clears active network state", "[p25][h REQUIRE_FALSE(HostTestHooks::p25NetworkWatchdog(*harness.m_control).isRunning()); } +TEST_CASE("P25 telephone interconnect permit survives voice hang until explicitly released", "[p25][host][control][telephone]") +{ + P25HostHarness harness(false); + harness.m_control->permittedTG(2001U, false, true); + HostTestHooks::p25SetRFCall(*harness.m_control, 1001U, 2001U); + + HostTestHooks::p25RFTGHang(*harness.m_control).start(); + HostTestHooks::p25RFTGHang(*harness.m_control).clock( + expireTimerTicks(HostTestHooks::p25RFTGHang(*harness.m_control))); + harness.m_control->clock(); + + REQUIRE(HostTestHooks::p25RFState(*harness.m_control) == RS_RF_LISTENING); + REQUIRE(HostTestHooks::p25PermittedDstId(*harness.m_control) == 2001U); + + harness.m_control->permittedTG(0U); + REQUIRE(HostTestHooks::p25PermittedDstId(*harness.m_control) == 0U); +} + +TEST_CASE("P25 ordinary permit is cleared when voice hang expires", "[p25][host][control][telephone]") +{ + P25HostHarness harness(false); + harness.m_control->permittedTG(2001U); + HostTestHooks::p25SetRFCall(*harness.m_control, 1001U, 2001U); + + HostTestHooks::p25RFTGHang(*harness.m_control).start(); + HostTestHooks::p25RFTGHang(*harness.m_control).clock( + expireTimerTicks(HostTestHooks::p25RFTGHang(*harness.m_control))); + harness.m_control->clock(); + + REQUIRE(HostTestHooks::p25RFState(*harness.m_control) == RS_RF_LISTENING); + REQUIRE(HostTestHooks::p25PermittedDstId(*harness.m_control) == 0U); +} + TEST_CASE("P25 clearRFReject returns rejected RF state to listening", "[p25][host][control][rf]") { P25HostHarness harness;