Tomas/connection raii initial#413
Draft
ProfessorTom wants to merge 147 commits into
Draft
Conversation
…tart unit testing
- New ctor: TCPThread(host, port, initialPacket, parent) - Creates QSslSocket early - Connects connected/errorOccurred/stateChanged signals - Stores host/port for run() - Sets m_managedByConnection = true
- [[nodiscard]] bool isValid() const - Checks clientConnection null, error codes, socket state - Adds detailed qDebug/qWarning logging for failure reasons
- Add null check for clientConnection before calling close() - Add waitForDisconnected(1000) for clean shutdown when socket exists - Log warning if called with null socket - Prevents potential segfault in dtor when socket not initialized
- Add if (clientConnection) guard before close() - Only call waitForDisconnected() if socket is not UnconnectedState or ClosingState - Log when wait is skipped or socket is null - Eliminates Qt warning about waitForDisconnected in UnconnectedState - Prevents potential null dereference in Connection dtor
…safe cleanup - Update ctor to pass host/port/initialPacket - Add public start() method with isValid() check - Auto-start in ctor - Add send(), isConnected(), isSecure() - Forward key TCPThread signals - Add m_threadStarted flag for safe dtor cleanup
- Add testThreadStartsAndStops() to verify no crash on start/dtor - Give thread time to start with QTest::qWait(500) - Check isConnected() (with fallback for Connecting state)
…plication - Link packet.cpp, tcpthread.cpp, sendpacketbutton.cpp - Add Qt6::Network for QSslSocket/QTcpSocket - Switch to per test class QApplication isolation with runGuiTest/runNonGuiTest
- Moved prepareForPersistentLoop() and persistentConnectionLoop() into new file src/persistentLoopConnection.cpp - Moved corresponding unit tests to persistentconnectionlooptests.cpp - Updated test runner and both CMakeLists.txt files - Fixed include paths for unit tests This reduces the size of tcpthread.cpp and sets up for targeted refactoring of the persistent loop logic.
- 5 tests now cover major paths: * normal data/response flow * idle status emission * immediate exit on closeRequest * exit on connection broken * final cleanup behavior - Provides safety net before we begin extracting smaller functions - Minor test helper improvements
- Extracted cleanup logic into cleanupAfterPersistentConnectionLoop() - Added testCleanupAfterPersistentConnectionLoop_whenClientConnectionIsNull_emitsDisconnected() - Verified that "Disconnected" status is emitted even when clientConnection is nullptr - Added callCleanupAfterPersistentConnectionLoop() helper in TestTcpThreadClass - Minor test infrastructure improvements (call counts pattern started) This establishes the pattern we'll use for testing extracted functions.
- Extracted final socket cleanup logic into its own method - Added virtual deleteSocketLater() wrapper for testability - Added three unit tests for cleanup behavior: * Null clientConnection case * Normal (non-managed) happy path * Managed-by-Connection case (verifies no deleteLater) - Updated TestTcpThreadClass with getMockSocket() helper and proper base member handling - Removed name-hiding clientConnection member This establishes the extraction + testing pattern we'll use for the rest of persistentConnectionLoop().
- Added virtual getPeerAddress() accessor to enable test mocking - Extracted getPeerAddressAsString() to eliminate duplicated address formatting code - Removed unused ipMode calculation from persistentConnectionLoop() - Updated both fromIP assignments to use the new helper method - Added unit tests for IPv4 and IPv6 address formatting paths - Updated TestTcpThreadClass with proper overrides and mock support This reduces duplication and makes the main loop significantly cleaner and more maintainable.
- Extracted packet sending logic into sendCurrentPacket() - Added two unit tests: * testSendCurrentPacket_emitsConnectionStatusWhenDataExists() * testSendCurrentPacket_emitsSentPacketWhenDataExists() - Added callSendCurrentPacket() helper and call count tracking in TestTcpThreadClass This continues the incremental extraction of persistentConnectionLoop(), making the main loop more readable.
- Added testSendCurrentPacket_doesNothingWhenNoDataToSend() - Verifies that no status or packetSent signals are emitted when sendPacket has no data - Completes basic behavior coverage for the extracted sendCurrentPacket() function
- Extracted the receiveBeforeSend logic into its own method `handleReceiveBeforeSend()` - Added virtual `readSocketData()` accessor to allow mocking `readAll()` - Added three unit tests for the new function: * testHandleReceiveBeforeSend_whenDataReceived_processesAndSendsResponse() * testHandleReceiveBeforeSend_whenNoDataReceived_doesNothing() * testHandleReceiveBeforeSend_setsCorrectPacketFields() - Updated TestTcpThreadClass with `callHandleReceiveBeforeSend()` helper and mock support This continues the incremental extraction of persistentConnectionLoop(), significantly improving readability.
- Extracted metadata setup + initial socket drain into buildReceivedPacket() - Renamed internal variable to receivedPacket for clarity - Switched to existing isSocketEncrypted(*clientSocket()) wrapper - Added testBuildReceivedPacket_setsSSLWhenSocketIsEncrypted() to explicitly cover the SSL branch in the new function Part of incremental refactoring of persistentConnectionLoop().
- Updated handleReceiveBeforeSend() to use existing isSocketEncrypted(*clientSocket()) wrapper instead of direct clientSocket()->isEncrypted() call (non-virtual method) - Added dedicated unit test testHandleReceiveBeforeSend_setsSSLWhenSocketIsEncrypted() to verify the SSL branch is correctly handled - Added QCOMPARE(received.tcpOrUdp, "TCP") assertion to testHandleReceiveBeforeSend_setsCorrectPacketFields() for completeness Part of ongoing extraction and test coverage improvements to persistentConnectionLoop().
- Extracted the post-send logic (disconnect handling, packetSent emission, writeResponse, second read + final response handling) into its own focused method. - Added four targeted unit tests covering: * Non-persistent connection (disconnecting status) * Normal case (emitting received packet) * receiveBeforeSend only emits when data is present * Second read / response-to-reply case with different data - Added callHandleResponseAfterSend() helper in TestTcpThreadClass - Minor test cleanup and assertions This significantly reduces the complexity of persistentConnectionLoop() and improves test coverage for the extracted behavior.
established pattern in this section of code.
Extracted the decision logic for breaking the persistent connection loop into its own const method. This improves readability of the main loop and makes the behavior unit-testable. - Added TCPThread::shouldBreakPersistentLoop() - Updated test double (TestTcpThreadClass) to support the new method - Added comprehensive unit tests for both persistent and non-persistent cases
…ent connections - Introduce `resetPacketForPersistentLoop()` which clears the sendPacket (via clear()) but explicitly re-sets `persistent = true` so the loop continues correctly. - Add comprehensive unit test `testResetPacketForPersistentLoop()` verifying all fields are reset to defaults (except persistent), and that the timestamp is properly updated to a newer value. - Expose method in TCPThread and TestTcpThreadClass. This prevents stale packet data from persisting across iterations in persistent TCP/UDP loops while maintaining the connection state.
- Replace manual `!sendPacket.persistent` check + else-branch with dedicated `shouldBreakPersistentLoop()` helper - Introduce `resetPacketForPersistentLoop()` to prepare for the next iteration Makes the loop intent clearer and centralizes the persistent-loop decision/reset logic.
- Added BaseTcpThread as the foundation for the upcoming OutgoingTcpThread and IncomingTcpThread split. - Implemented key virtual getters: isValid(), getSocket(), isSocketEncrypted(), getPeerPort(), getLocalPort(), getIPConnectionProtocol(), getPeerAddressAsString(), getSocketPeerAddress(). - Added comprehensive unit tests covering null cases, real sockets, IPv4/IPv6 behavior, and edge cases. - Updated test infrastructure (BaseTcpThreadTestDouble, MockSslSocket, CMake). - Cleaned up Packet::removeIPv6Mapping() for better IPv6 handling. This is an intermediate step toward removing the monolithic TCPThread.
- Added OutgoingTcpThread with main constructor taking socket + Packet - Added validation for destination address and port in constructor - Implemented isValid() with outgoing-specific checks + base delegation - Added getDestinationAddress() and getDestinationPort() getters - Added comprehensive unit tests for constructor validation and isValid() - Updated BaseTcpThread with virtual isSocketValid() for better testability - Added OutgoingTcpThreadTestDouble This establishes the foundation for outgoing client connections.
- Added convenience constructor `OutgoingTcpThread(const Packet& packetToSend, QObject* parent = nullptr)` that internally creates the QSslSocket and delegates to the main constructor. - Updated all unit tests to use the new convenience constructor (much cleaner). - Updated OutgoingTcpThreadTestDouble to support the new constructor. - Removed manual QSslSocket creation from most tests. This improves usability for production code while keeping the socket-taking constructor available for tests.
…nit tests - Introduced `bool Packet::isValidForSending(QString* errorMessage = nullptr)` to centralize validation logic for packets before sending. - Validates the three critical fields: non-empty `toIP`, positive `port`, and non-empty data (`getByteArray()`). - Returns detailed human-readable error messages via optional out-parameter when validation fails. - Added `PacketTests` class with 8 focused unit tests covering all success and failure paths, including error message verification. - Wired new tests into the unit test runner. This enables defensive runtime checks before sending and makes future packet validation consistent and testable.
…tests - Fixed copy constructor to include `resolvedIP` (was missing, causing equality tests to fail) - Implemented `Packet::operator==()` comparing all meaningful fields (timestamp intentionally excluded) - Implemented `Packet::operator!=()` in terms of `operator==()` - Added thorough unit tests: - Identical packets are equal - Timestamp is ignored for equality - Data-driven tests covering all 16 fields in `operator==()` - Tests for `operator!=()` behavior This makes `Packet` a proper value type and improves reliability of tests that compare packets (especially in the new TCP thread code).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Before submitting a pull request:
This will likely be a Cthulhu PR that will need to be broken up into a few smaller PRs once the work is done. Unfortunately, the way through is a long running branch in the short term.