From 47fff42173fe80ed6731b6d22282c1fa384317b8 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 14 Aug 2026 10:32:47 -0400 Subject: [PATCH] add support for FNE tests via dvmtests harness; --- CMakeLists.txt | 9 +- src/fne/ActivityLog.h | 2 +- src/fne/FNEMain.h | 2 +- src/fne/HostFNE.cpp | 2 +- src/fne/HostFNE.h | 11 +- src/fne/lookups/AffiliationLookup.h | 2 +- src/fne/network/FNEPeerConnection.h | 11 ++ src/fne/network/MetadataNetwork.h | 7 + src/fne/network/PeerNetwork.h | 2 +- src/fne/network/TrafficNetwork.cpp | 2 + src/fne/network/TrafficNetwork.h | 7 + src/fne/network/packet/KeysInventory.cpp | 2 + src/host/dmr/Control.h | 4 + src/host/dmr/Slot.h | 4 + src/host/nxdn/Control.h | 4 + src/host/p25/Control.h | 4 + src/host/p25/packet/Voice.h | 4 + src/host/p25/packet/phase2/Voice.h | 4 + src/host/p25/phase2/Control.h | 8 + src/host/p25/phase2/Slot.h | 4 + tests/CMakeLists.txt | 2 + tests/fne/FNENetwork_Tests.cpp | 91 +++++++++++ tests/fne/FNETestHooks.cpp | 183 +++++++++++++++++++++++ tests/fne/FNETestHooks.h | 88 +++++++++++ 24 files changed, 450 insertions(+), 9 deletions(-) create mode 100644 tests/fne/FNENetwork_Tests.cpp create mode 100644 tests/fne/FNETestHooks.cpp create mode 100644 tests/fne/FNETestHooks.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 614f9c1e..8d6fcc56 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -278,10 +278,15 @@ if (ENABLE_TESTS) target_compile_definitions(asio::asio INTERFACE "ASIO_STANDALONE") target_link_libraries(asio::asio INTERFACE Threads::Threads) - add_executable(dvmtests ${common_INCLUDE} ${dvmhost_SRC} ${dvmtests_SRC}) + set(dvmfnetest_SRC ${dvmfne_SRC}) + list(FILTER dvmfnetest_SRC EXCLUDE REGEX "/FNEMain\\.cpp$") + list(FILTER dvmfnetest_SRC EXCLUDE REGEX "/ActivityLog\\.cpp$") + list(FILTER dvmfnetest_SRC EXCLUDE REGEX "/HostFNE\\.cpp$") + list(FILTER dvmfnetest_SRC EXCLUDE REGEX "/fne/restapi/.*\\.cpp$") + add_executable(dvmtests ${common_INCLUDE} ${dvmhost_SRC} ${dvmfnetest_SRC} ${dvmtests_SRC}) target_compile_definitions(dvmtests PUBLIC -DCATCH2_TEST_COMPILATION) target_link_libraries(dvmtests PRIVATE Catch2::Catch2WithMain common ${OPENSSL_LIBRARIES} asio::asio Threads::Threads util) - target_include_directories(dvmtests PRIVATE ${OPENSSL_INCLUDE_DIR} src src/host tests) + target_include_directories(dvmtests PRIVATE ${OPENSSL_INCLUDE_DIR} src src/host src/fne tests) catch_discover_tests(dvmtests) endif (ENABLE_TESTS) diff --git a/src/fne/ActivityLog.h b/src/fne/ActivityLog.h index 5800f76c..28be8795 100644 --- a/src/fne/ActivityLog.h +++ b/src/fne/ActivityLog.h @@ -16,7 +16,7 @@ #if !defined(__ACTIVITY_LOG_H__) #define __ACTIVITY_LOG_H__ -#include "Defines.h" +#include "fne/Defines.h" #include diff --git a/src/fne/FNEMain.h b/src/fne/FNEMain.h index 9d279160..55aedef5 100644 --- a/src/fne/FNEMain.h +++ b/src/fne/FNEMain.h @@ -16,7 +16,7 @@ #if !defined(__FNE_MAIN_H__) #define __FNE_MAIN_H__ -#include "Defines.h" +#include "fne/Defines.h" #include diff --git a/src/fne/HostFNE.cpp b/src/fne/HostFNE.cpp index 68adea3e..c84ce180 100644 --- a/src/fne/HostFNE.cpp +++ b/src/fne/HostFNE.cpp @@ -7,7 +7,7 @@ * Copyright (C) 2023-2026 Bryan Biedenkapp, N2PLL * */ -#include "Defines.h" +#include "fne/Defines.h" #include "common/network/udp/Socket.h" #include "common/Log.h" #include "common/StopWatch.h" diff --git a/src/fne/HostFNE.h b/src/fne/HostFNE.h index 435d2f81..77000a77 100644 --- a/src/fne/HostFNE.h +++ b/src/fne/HostFNE.h @@ -16,7 +16,7 @@ #if !defined(__HOST_FNE_H__) #define __HOST_FNE_H__ -#include "Defines.h" +#include "fne/Defines.h" #include "common/lookups/RadioIdLookup.h" #include "common/lookups/TalkgroupRulesLookup.h" #include "common/lookups/PeerListLookup.h" @@ -27,7 +27,7 @@ #include "network/TrafficNetwork.h" #include "network/MetadataNetwork.h" #include "network/PeerNetwork.h" -#include "restapi/RESTAPI.h" +#include "fne/restapi/RESTAPI.h" #include "CryptoContainer.h" #include @@ -38,6 +38,10 @@ // Class Prototypes // --------------------------------------------------------------------------- +#if defined(CATCH2_TEST_COMPILATION) +class FNETestHooks; +#endif + namespace network { namespace callhandler { class HOST_SW_API TagDMRData; } } namespace network { namespace callhandler { namespace packetdata { class HOST_SW_API DMRPacketData; } } } namespace network { namespace callhandler { class HOST_SW_API TagP25Data; } } @@ -81,6 +85,9 @@ public: int run(); private: +#if defined(CATCH2_TEST_COMPILATION) + friend class FNETestHooks; +#endif const std::string& m_confFile; yaml::Node m_conf; diff --git a/src/fne/lookups/AffiliationLookup.h b/src/fne/lookups/AffiliationLookup.h index f18c0270..6c9f7a00 100644 --- a/src/fne/lookups/AffiliationLookup.h +++ b/src/fne/lookups/AffiliationLookup.h @@ -20,7 +20,7 @@ #if !defined(__FNE_AFFILIATION_LOOKUP_H__) #define __FNE_AFFILIATION_LOOKUP_H__ -#include "Defines.h" +#include "fne/Defines.h" #include "common/lookups/AffiliationLookup.h" #include "common/lookups/ChannelLookup.h" diff --git a/src/fne/network/FNEPeerConnection.h b/src/fne/network/FNEPeerConnection.h index d1c92ab7..237d6ed1 100644 --- a/src/fne/network/FNEPeerConnection.h +++ b/src/fne/network/FNEPeerConnection.h @@ -24,6 +24,14 @@ #include #include +// --------------------------------------------------------------------------- +// Class Prototypes +// --------------------------------------------------------------------------- + +#if defined(CATCH2_TEST_COMPILATION) +class FNETestHooks; +#endif + namespace network { // --------------------------------------------------------------------------- @@ -275,6 +283,9 @@ namespace network DECLARE_PROPERTY_PLAIN(json::object, config); private: +#if defined(CATCH2_TEST_COMPILATION) + friend class ::FNETestHooks; +#endif mutable std::mutex m_peerLockMtx; std::map m_jitterBuffers; diff --git a/src/fne/network/MetadataNetwork.h b/src/fne/network/MetadataNetwork.h index 45797e46..04918ad0 100644 --- a/src/fne/network/MetadataNetwork.h +++ b/src/fne/network/MetadataNetwork.h @@ -30,6 +30,10 @@ // Class Prototypes // --------------------------------------------------------------------------- +#if defined(CATCH2_TEST_COMPILATION) +class FNETestHooks; +#endif + class HOST_SW_API HostFNE; namespace network @@ -99,6 +103,9 @@ namespace network void close() override; private: +#if defined(CATCH2_TEST_COMPILATION) + friend class ::FNETestHooks; +#endif friend class TrafficNetwork; TrafficNetwork* m_trafficNetwork; HostFNE* m_host; diff --git a/src/fne/network/PeerNetwork.h b/src/fne/network/PeerNetwork.h index 608661a4..56325133 100644 --- a/src/fne/network/PeerNetwork.h +++ b/src/fne/network/PeerNetwork.h @@ -16,7 +16,7 @@ #if !defined(__PEER_NETWORK_H__) #define __PEER_NETWORK_H__ -#include "Defines.h" +#include "fne/Defines.h" #include "common/lookups/PeerListLookup.h" #include "common/network/Network.h" #include "common/network/PacketBuffer.h" diff --git a/src/fne/network/TrafficNetwork.cpp b/src/fne/network/TrafficNetwork.cpp index 52e5b129..2fc8263e 100644 --- a/src/fne/network/TrafficNetwork.cpp +++ b/src/fne/network/TrafficNetwork.cpp @@ -195,7 +195,9 @@ TrafficNetwork::TrafficNetwork(HostFNE* host, const std::string& address, uint16 ** Initialize Threads */ +#if !defined(CATCH2_TEST_COMPILATION) Thread::runAsThread(this, threadParrotHandler); +#endif } /* Finalizes a instance of the TrafficNetwork class. */ diff --git a/src/fne/network/TrafficNetwork.h b/src/fne/network/TrafficNetwork.h index 777e65fb..395de81a 100644 --- a/src/fne/network/TrafficNetwork.h +++ b/src/fne/network/TrafficNetwork.h @@ -57,6 +57,10 @@ // Class Prototypes // --------------------------------------------------------------------------- +#if defined(CATCH2_TEST_COMPILATION) +class FNETestHooks; +#endif + class HOST_SW_API HostFNE; class HOST_SW_API RESTAPI; namespace network { namespace callhandler { class HOST_SW_API TagDMRData; } } @@ -310,6 +314,9 @@ namespace network void setPeerReplica(bool replica); private: +#if defined(CATCH2_TEST_COMPILATION) + friend class ::FNETestHooks; +#endif friend class MetadataNetwork; friend class callhandler::TagDMRData; friend class callhandler::packetdata::DMRPacketData; diff --git a/src/fne/network/packet/KeysInventory.cpp b/src/fne/network/packet/KeysInventory.cpp index 5ae221a5..23b1466d 100644 --- a/src/fne/network/packet/KeysInventory.cpp +++ b/src/fne/network/packet/KeysInventory.cpp @@ -14,6 +14,8 @@ #include "fne/ActivityLog.h" #include "HostFNE.h" +#include + using namespace network; // --------------------------------------------------------------------------- diff --git a/src/host/dmr/Control.h b/src/host/dmr/Control.h index 1ff0f382..f1763ac5 100644 --- a/src/host/dmr/Control.h +++ b/src/host/dmr/Control.h @@ -43,7 +43,9 @@ // Class Prototypes // --------------------------------------------------------------------------- +#if defined(CATCH2_TEST_COMPILATION) class HostTestHooks; +#endif namespace dmr { @@ -305,7 +307,9 @@ namespace dmr private: friend class Slot; +#if defined(CATCH2_TEST_COMPILATION) friend class ::HostTestHooks; +#endif bool m_authoritative; bool m_supervisor; diff --git a/src/host/dmr/Slot.h b/src/host/dmr/Slot.h index 3bc8e817..27ec24e0 100644 --- a/src/host/dmr/Slot.h +++ b/src/host/dmr/Slot.h @@ -41,7 +41,9 @@ // Class Prototypes // --------------------------------------------------------------------------- +#if defined(CATCH2_TEST_COMPILATION) class HostTestHooks; +#endif namespace dmr { @@ -345,7 +347,9 @@ namespace dmr private: friend class Control; +#if defined(CATCH2_TEST_COMPILATION) friend class ::HostTestHooks; +#endif friend class packet::Voice; packet::Voice* m_voice; friend class packet::Data; diff --git a/src/host/nxdn/Control.h b/src/host/nxdn/Control.h index c392acec..fe8a1237 100644 --- a/src/host/nxdn/Control.h +++ b/src/host/nxdn/Control.h @@ -51,7 +51,9 @@ // Class Prototypes // --------------------------------------------------------------------------- +#if defined(CATCH2_TEST_COMPILATION) class HostTestHooks; +#endif namespace nxdn { @@ -266,7 +268,9 @@ namespace nxdn private: friend class packet::Voice; +#if defined(CATCH2_TEST_COMPILATION) friend class ::HostTestHooks; +#endif packet::Voice* m_voice; friend class packet::Data; packet::Data* m_data; diff --git a/src/host/p25/Control.h b/src/host/p25/Control.h index 3261a298..3f0fa07e 100644 --- a/src/host/p25/Control.h +++ b/src/host/p25/Control.h @@ -56,7 +56,9 @@ // Class Prototypes // --------------------------------------------------------------------------- +#if defined(CATCH2_TEST_COMPILATION) class HostTestHooks; +#endif namespace p25 { @@ -284,7 +286,9 @@ namespace p25 private: friend class packet::Voice; +#if defined(CATCH2_TEST_COMPILATION) friend class ::HostTestHooks; +#endif packet::Voice* m_voice; friend class packet::Data; packet::Data* m_data; diff --git a/src/host/p25/packet/Voice.h b/src/host/p25/packet/Voice.h index 7a4be0c3..4acbc3d0 100644 --- a/src/host/p25/packet/Voice.h +++ b/src/host/p25/packet/Voice.h @@ -31,7 +31,9 @@ // Class Prototypes // --------------------------------------------------------------------------- +#if defined(CATCH2_TEST_COMPILATION) class HostTestHooks; +#endif namespace p25 { @@ -87,7 +89,9 @@ namespace p25 protected: friend class packet::ControlSignaling; friend class p25::Control; +#if defined(CATCH2_TEST_COMPILATION) friend class ::HostTestHooks; +#endif Control* m_p25; uint32_t m_rfFrames; diff --git a/src/host/p25/packet/phase2/Voice.h b/src/host/p25/packet/phase2/Voice.h index 4ec0241c..57aad1f5 100644 --- a/src/host/p25/packet/phase2/Voice.h +++ b/src/host/p25/packet/phase2/Voice.h @@ -28,7 +28,9 @@ // Class Prototypes // --------------------------------------------------------------------------- +#if defined(CATCH2_TEST_COMPILATION) class HostTestHooks; +#endif namespace p25 { @@ -102,7 +104,9 @@ namespace p25 private: friend class p25::phase2::Slot; +#if defined(CATCH2_TEST_COMPILATION) friend class ::HostTestHooks; +#endif p25::phase2::Slot* m_slot; diff --git a/src/host/p25/phase2/Control.h b/src/host/p25/phase2/Control.h index f8eaeec3..0cc4dd30 100644 --- a/src/host/p25/phase2/Control.h +++ b/src/host/p25/phase2/Control.h @@ -26,7 +26,13 @@ #include +// --------------------------------------------------------------------------- +// Class Prototypes +// --------------------------------------------------------------------------- + +#if defined(CATCH2_TEST_COMPILATION) class HostTestHooks; +#endif namespace p25 { @@ -172,7 +178,9 @@ namespace p25 private: friend class Slot; friend class packet::phase2::Voice; +#if defined(CATCH2_TEST_COMPILATION) friend class ::HostTestHooks; +#endif /* Interfaces */ modem::Modem* m_modem; diff --git a/src/host/p25/phase2/Slot.h b/src/host/p25/phase2/Slot.h index 6d9b1bed..9118674a 100644 --- a/src/host/p25/phase2/Slot.h +++ b/src/host/p25/phase2/Slot.h @@ -34,7 +34,9 @@ // Class Prototypes // --------------------------------------------------------------------------- +#if defined(CATCH2_TEST_COMPILATION) class HostTestHooks; +#endif namespace p25 { @@ -178,7 +180,9 @@ namespace p25 lookups::P25AffiliationLookup* affiliations, ::lookups::RadioIdLookup* ridLookup, ::lookups::TalkgroupRulesLookup* tidLookup); private: +#if defined(CATCH2_TEST_COMPILATION) friend class ::HostTestHooks; +#endif friend class phase2::Control; friend class packet::phase2::Voice; packet::phase2::Voice m_voice; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 82858b03..61ec313a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -18,4 +18,6 @@ file(GLOB dvmtests_SRC "tests/p25/*.cpp" "tests/nxdn/*.cpp" "tests/network/*.cpp" + "tests/fne/*.h" + "tests/fne/*.cpp" ) diff --git a/tests/fne/FNENetwork_Tests.cpp b/tests/fne/FNENetwork_Tests.cpp new file mode 100644 index 00000000..91bcd46b --- /dev/null +++ b/tests/fne/FNENetwork_Tests.cpp @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Test Suite + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2026 Bryan Biedenkapp, N2PLL + * + */ + +#include + +#include "fne/FNETestHooks.h" +#include "fne/HostFNE.h" + +#include +#include + +// --------------------------------------------------------------------------- +// Class Declaration +// --------------------------------------------------------------------------- + +/** + * @brief Test harness for FNE network tests. + */ +class FNEHarness { +public: + /** + * @brief Initializes a new instance of the FNEHarness class. + */ + FNEHarness() : + configFile("fne-test.yml"), + host(configFile), + traffic(&host, "127.0.0.1", 62031U, 999999U, "test-password", "test-fne", + false, false, false, false, true, true, true, true, true, + 0U, false, true, true, 5U, 10U, 2U), + metadata(&host, &traffic, "127.0.0.1", 62032U, 2U) + { + /* stub */ + } + /** + * @brief Finalizes a instance of the FNEHarness class. + */ + ~FNEHarness() + { + FNETestHooks::clearPeers(traffic); + } + + std::string configFile; + HostFNE host; + network::TrafficNetwork traffic; + network::MetadataNetwork metadata; +}; + +TEST_CASE("FNE test hooks expose peer connection state", "[fne][hooks]") +{ + FNEHarness harness; + FNETestHooks::addPeer(harness.traffic, 1001U, network::NET_STAT_WAITING_AUTHORISATION); + + REQUIRE(FNETestHooks::hasPeer(harness.traffic, 1001U)); + REQUIRE(FNETestHooks::peerCount(harness.traffic) == 1U); + REQUIRE(FNETestHooks::peerState(harness.traffic, 1001U) == network::NET_STAT_WAITING_AUTHORISATION); +} + +TEST_CASE("FNE traffic dispatcher rejects truncated pre-authentication packets", "[fne][hooks][security]") +{ + FNEHarness harness; + FNETestHooks::addPeer(harness.traffic, 1001U, network::NET_STAT_WAITING_AUTHORISATION); + + REQUIRE_NOTHROW(FNETestHooks::dispatchTraffic(harness.traffic, network::NET_FUNC::RPTK, + network::NET_SUBFUNC::NOP, 1001U, std::vector(8U))); + REQUIRE(FNETestHooks::peerState(harness.traffic, 1001U) == network::NET_STAT_WAITING_AUTHORISATION); + + REQUIRE_NOTHROW(FNETestHooks::dispatchTraffic(harness.traffic, network::NET_FUNC::RPTC, + network::NET_SUBFUNC::NOP, 1001U, std::vector(8U))); + REQUIRE(FNETestHooks::peerState(harness.traffic, 1001U) == network::NET_STAT_WAITING_AUTHORISATION); +} + +TEST_CASE("FNE metadata dispatcher rejects truncated log transfers", "[fne][hooks][security]") +{ + FNEHarness harness; + FNETestHooks::addPeer(harness.traffic, 1001U, network::NET_STAT_RUNNING, true); + + REQUIRE_NOTHROW(FNETestHooks::dispatchMetadata(harness.traffic, harness.metadata, + network::NET_FUNC::TRANSFER, network::NET_SUBFUNC::TRANSFER_SUBFUNC_ACTIVITY, + 1001U, std::vector(11U))); + REQUIRE_NOTHROW(FNETestHooks::dispatchMetadata(harness.traffic, harness.metadata, + network::NET_FUNC::TRANSFER, network::NET_SUBFUNC::TRANSFER_SUBFUNC_DIAG, + 1001U, std::vector(10U))); + REQUIRE(FNETestHooks::peerState(harness.traffic, 1001U) == network::NET_STAT_RUNNING); +} diff --git a/tests/fne/FNETestHooks.cpp b/tests/fne/FNETestHooks.cpp new file mode 100644 index 00000000..355a70d2 --- /dev/null +++ b/tests/fne/FNETestHooks.cpp @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Test Suite + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2026 Bryan Biedenkapp, N2PLL + * + */ +#include "fne/FNETestHooks.h" +#include "fne/HostFNE.h" + +#include +#include +#include + +using namespace network; + +// --------------------------------------------------------------------------- +// Global Variables +// --------------------------------------------------------------------------- + +bool g_promiscuousHub = false; + +// --------------------------------------------------------------------------- +// Public Class Members +// --------------------------------------------------------------------------- + +/** + * @brief Initializes an instance of the HostFNE class. + */ +HostFNE::HostFNE(const std::string& confFile) : + m_confFile(confFile), m_conf(), m_network(nullptr), m_mdNetwork(nullptr), + m_vtunEnabled(false), m_packetDataMode(PacketDataMode::PROJECT25), +#if !defined(_WIN32) + m_tun(nullptr), +#endif + m_dmrEnabled(false), m_p25Enabled(false), m_p25P2Enabled(false), + m_nxdnEnabled(false), m_analogEnabled(false), m_ridLookup(nullptr), + m_tidLookup(nullptr), m_peerListLookup(nullptr), m_adjSiteMapLookup(nullptr), + m_cryptoLookup(nullptr), m_peerNetworks(), m_pingTime(5U), m_maxMissedPings(5U), + m_updateLookupTime(10U), m_peerReplicaSavesACL(false), + m_allowActivityTransfer(false), m_allowDiagnosticTransfer(false), m_RESTAPI(nullptr) +{ + /* stub */ +} + +/** + * @brief Finalizes an instance of the HostFNE class. + */ +HostFNE::~HostFNE() = default; + +/** + * @brief Adds a peer to the specified TrafficNetwork. + * @param network The TrafficNetwork instance. + * @param peerId The ID of the peer to add. + * @param state The connection state of the peer. + * @param connected Whether the peer is connected. + * @return Reference to the added FNEPeerConnection. + */ +FNEPeerConnection& FNETestHooks::addPeer(TrafficNetwork& network, uint32_t peerId, + NET_CONN_STATUS state, bool connected) +{ + FNEPeerConnection* connection = new FNEPeerConnection(); + connection->m_id = peerId; + connection->m_connectionState = state; + connection->m_connected = connected; + network.m_peers[peerId] = connection; + return *connection; +} + +// --------------------------------------------------------------------------- +// Static Class Members +// --------------------------------------------------------------------------- + +/** + * @brief Checks if the specified peer exists in the TrafficNetwork. + * @param network The TrafficNetwork instance. + * @param peerId The ID of the peer to check. + * @return True if the peer exists, false otherwise. + */ +bool FNETestHooks::hasPeer(const TrafficNetwork& network, uint32_t peerId) +{ + return network.m_peers.find(peerId) != network.m_peers.end(); +} + +/** + * @brief Retrieves the number of peers in the TrafficNetwork. + * @param network The TrafficNetwork instance. + * @return The number of peers. + */ +size_t FNETestHooks::peerCount(const TrafficNetwork& network) +{ + return network.m_peers.size(); +} + +/** + * @brief Retrieves the connection state of the specified peer. + * @param network The TrafficNetwork instance. + * @param peerId The ID of the peer. + * @return The connection state of the peer. + */ +NET_CONN_STATUS FNETestHooks::peerState(const TrafficNetwork& network, uint32_t peerId) +{ + auto it = network.m_peers.find(peerId); + if (it == network.m_peers.end() || it->second == nullptr) + throw std::out_of_range("peer does not exist"); + return it->second->connectionState(); +} + +/** + * @brief Clears all peers from the specified TrafficNetwork. + * @param network The TrafficNetwork instance. + */ +void FNETestHooks::clearPeers(TrafficNetwork& network) +{ + for (auto peer : network.m_peers) + delete peer.second; + network.m_peers.clear(); +} + +/** + * @brief Creates a NetPacketRequest for the specified network, metadata, function, subFunction, peerId, and packet. + * @param network The TrafficNetwork instance. + * @param metadata The MetadataNetwork instance (can be nullptr). + * @param function The network function. + * @param subFunction The network sub-function. + * @param peerId The ID of the peer. + * @param packet The packet data. + * @return Pointer to the created NetPacketRequest. + */ +static NetPacketRequest* makeRequest(TrafficNetwork& network, MetadataNetwork* metadata, + NET_FUNC::ENUM function, NET_SUBFUNC::ENUM subFunction, uint32_t peerId, + const std::vector& packet) +{ + NetPacketRequest* request = new NetPacketRequest(); + request->obj = &network; + request->metadataObj = metadata; + request->peerId = peerId; + request->fneHeader.setFunction(function); + request->fneHeader.setSubFunction(subFunction); + request->fneHeader.setPeerId(peerId); + request->fneHeader.setStreamId(1U); + request->rtpHeader.setSSRC(peerId); + request->pktRxTime = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()).count(); + request->length = static_cast(packet.size()); + if (!packet.empty()) { + request->buffer = new uint8_t[packet.size()]; + ::memcpy(request->buffer, packet.data(), packet.size()); + } + return request; +} + +/** + * @brief Dispatches a traffic packet to the specified TrafficNetwork. + * @param network The TrafficNetwork instance. + * @param function The network function. + * @param subFunction The network sub-function. + * @param peerId The ID of the peer. + * @param packet The packet data. + */ +void FNETestHooks::dispatchTraffic(TrafficNetwork& network, NET_FUNC::ENUM function, + NET_SUBFUNC::ENUM subFunction, uint32_t peerId, const std::vector& packet) +{ + TrafficNetwork::taskNetworkRx(makeRequest(network, nullptr, function, subFunction, peerId, packet)); +} + +/** + * @brief Dispatches a metadata packet to the specified TrafficNetwork. + * @param network The TrafficNetwork instance. + * @param metadata The MetadataNetwork instance. + * @param function The network function. + * @param subFunction The network sub-function. + * @param peerId The ID of the peer. + * @param packet The packet data. + */ +void FNETestHooks::dispatchMetadata(TrafficNetwork& network, MetadataNetwork& metadata, + NET_FUNC::ENUM function, NET_SUBFUNC::ENUM subFunction, uint32_t peerId, + const std::vector& packet) +{ + MetadataNetwork::taskNetworkRx(makeRequest(network, &metadata, function, subFunction, peerId, packet)); +} diff --git a/tests/fne/FNETestHooks.h b/tests/fne/FNETestHooks.h new file mode 100644 index 00000000..e9fc7436 --- /dev/null +++ b/tests/fne/FNETestHooks.h @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Test Suite + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2026 Bryan Biedenkapp, N2PLL + * + */ +#if !defined(__FNE_TEST_HOOKS_H__) +#define __FNE_TEST_HOOKS_H__ + +#include "fne/network/MetadataNetwork.h" +#include "fne/network/TrafficNetwork.h" + +#include +#include + +// --------------------------------------------------------------------------- +// Class Declaration +// --------------------------------------------------------------------------- + +/** + * @brief Test hooks for the FNE network components. + */ +class FNETestHooks { +public: + /** + * @brief Adds a peer to the specified TrafficNetwork. + * @param network The TrafficNetwork instance. + * @param peerId The ID of the peer to add. + * @param state The connection state of the peer. + * @param connected Whether the peer is connected. + * @return Reference to the added FNEPeerConnection. + */ + static network::FNEPeerConnection& addPeer(network::TrafficNetwork& network, uint32_t peerId, + network::NET_CONN_STATUS state, bool connected = false); + /** + * @brief Checks if the specified peer exists in the TrafficNetwork. + * @param network The TrafficNetwork instance. + * @param peerId The ID of the peer to check. + * @return True if the peer exists, false otherwise. + */ + static bool hasPeer(const network::TrafficNetwork& network, uint32_t peerId); + /** + * @brief Retrieves the number of peers in the TrafficNetwork. + * @param network The TrafficNetwork instance. + * @return The number of peers. + */ + static size_t peerCount(const network::TrafficNetwork& network); + /** + * @brief Retrieves the connection state of the specified peer. + * @param network The TrafficNetwork instance. + * @param peerId The ID of the peer. + * @return The connection state of the peer. + */ + static network::NET_CONN_STATUS peerState(const network::TrafficNetwork& network, uint32_t peerId); + /** + * @brief Clears all peers from the TrafficNetwork. + * @param network The TrafficNetwork instance. + */ + static void clearPeers(network::TrafficNetwork& network); + + /** + * @brief Dispatches a traffic packet to the specified peer in the TrafficNetwork. + * @param network The TrafficNetwork instance. + * @param function The network function. + * @param subFunction The network sub-function. + * @param peerId The ID of the peer. + * @param packet The packet data. + */ + static void dispatchTraffic(network::TrafficNetwork& network, network::NET_FUNC::ENUM function, + network::NET_SUBFUNC::ENUM subFunction, uint32_t peerId, const std::vector& packet); + /** + * @brief Dispatches a metadata packet to the specified peer in the TrafficNetwork. + * @param network The TrafficNetwork instance. + * @param metadata The MetadataNetwork instance. + * @param function The network function. + * @param subFunction The network sub-function. + * @param peerId The ID of the peer. + * @param packet The packet data. + */ + static void dispatchMetadata(network::TrafficNetwork& network, network::MetadataNetwork& metadata, + network::NET_FUNC::ENUM function, network::NET_SUBFUNC::ENUM subFunction, uint32_t peerId, + const std::vector& packet); +}; + +#endif // __FNE_TEST_HOOKS_H__