Skip to content

Tomas/connection raii initial#413

Draft
ProfessorTom wants to merge 147 commits into
dannagle:developmentfrom
ProfessorTom:tomas/connection-raii-initial
Draft

Tomas/connection raii initial#413
ProfessorTom wants to merge 147 commits into
dannagle:developmentfrom
ProfessorTom:tomas/connection-raii-initial

Conversation

@ProfessorTom
Copy link
Copy Markdown
Contributor

@ProfessorTom ProfessorTom commented Mar 6, 2026

Before submitting a pull request:

  • Did you fork from the development branch? yes
  • Are you submitting the pull request to the development branch? (not master) yes

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.

- 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).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant