🧪 [testing] Improve test coverage for DBusConnectionManager.cpp#290
🧪 [testing] Improve test coverage for DBusConnectionManager.cpp#290
Conversation
🎯 What: DBusConnectionManager relied directly on libdbus C API functions which made it impossible to mock and test effectively, leading to failures in isolated test environments. 📊 Coverage: Added a `DBusAPIWrapper` struct and `setDBusAPI` to `DBusConnectionManager` to inject mock function pointers, then updated `test_dbus_connection_manager_comprehensive.cpp` to supply a Mock DBus API that interacts with the existing `MockDBusConnection` class. ✨ Result: `DBusConnectionManager` unit tests and comprehensive tests now successfully execute, verifying reconnection logic, connection state monitoring, and edge cases under simulated D-Bus failures. Co-authored-by: segin <480709+segin@users.noreply.github.com>
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
There was a problem hiding this comment.
Pull request overview
Refactors D-Bus connection handling to support dependency injection of the libdbus C API (enabling mocking in unit tests) and routes connection unref through the injected API.
Changes:
- Added
DBusAPIWrapperand a configurable static API instance onDBusConnectionManager. - Updated
DBusConnectionManagerto call D-Bus operations through the injected wrapper instead of calling libdbus directly. - Updated
DBusConnectionDeleterto unref connections viaDBusConnectionManager::unrefConnection().
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| src/mpris/MPRISTypes.cpp | Routes DBus connection unref through DBusConnectionManager (but introduces an include-placement issue). |
| src/mpris/DBusConnectionManager.cpp | Implements the injected API wrapper and uses it for D-Bus operations. |
| include/mpris/DBusConnectionManager.h | Defines DBusAPIWrapper and exposes API injection / unref helpers. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // RAII deleters implementation | ||
| #include "mpris/DBusConnectionManager.h" | ||
|
|
||
| void DBusConnectionDeleter::operator()([[maybe_unused]] DBusConnection *conn) { |
There was a problem hiding this comment.
#include "mpris/DBusConnectionManager.h" is placed after the namespace PsyMP3 { namespace MPRIS { has already been opened, so the header’s own namespace PsyMP3 { namespace MPRIS { ... } } blocks will be included inside an existing namespace. This creates a nested PsyMP3::MPRIS::PsyMP3::MPRIS namespace and will break name lookup (and can fail to compile). Move this include up with the other includes (outside any namespace), or forward-declare the needed symbol and include in the translation unit scope.
| void DBusConnectionManager::setDBusAPI(const DBusAPIWrapper& api) { | ||
| s_dbus_api = api; | ||
| } |
There was a problem hiding this comment.
setDBusAPI() allows installing a DBusAPIWrapper with null function pointers, but call sites later dereference fields like connection_get_is_connected, error_init, etc. without null checks. This can lead to immediate crashes if a test (or any caller) provides a partial wrapper. Consider validating the wrapper in setDBusAPI() (e.g., assert/return error when any required pointer is null) or falling back to the default libdbus-backed implementation for missing entries.
| void DBusConnectionManager::setDBusAPI(const DBusAPIWrapper& api) { | ||
| s_dbus_api = api; | ||
| } | ||
|
|
||
| void DBusConnectionManager::unrefConnection(DBusConnection* conn) { | ||
| #ifdef HAVE_DBUS | ||
| if (conn && s_dbus_api.connection_unref) { | ||
| s_dbus_api.connection_unref(conn); |
There was a problem hiding this comment.
s_dbus_api is a mutable static used from instance methods (which may run concurrently across threads), but setDBusAPI() writes to it without any synchronization. If setDBusAPI() can be called after threads start, this is a data race. Either (a) restrict setDBusAPI() to test-only builds / call-before-use and document it clearly, or (b) protect reads/writes with a mutex / use an immutable shared_ptr-style swap.
There was a problem hiding this comment.
Code Review
This pull request introduces a DBusAPIWrapper structure to facilitate dependency injection for D-Bus operations within the DBusConnectionManager, replacing direct library calls with function pointers. The review identifies a critical thread-safety concern regarding unsynchronized access to the static API wrapper and recommends moving a new include directive to the top of the file to maintain standard code organization.
| #endif | ||
|
|
||
| void DBusConnectionManager::setDBusAPI(const DBusAPIWrapper& api) { | ||
| s_dbus_api = api; |
There was a problem hiding this comment.
The static variable s_dbus_api is accessed by multiple threads without synchronization, which creates a data race. This function writes to s_dbus_api, while other methods read from it. The assignment to s_dbus_api is not atomic and can lead to torn reads and undefined behavior.
To ensure thread safety, access to s_dbus_api should be protected by a mutex. You should add a static std::mutex s_dbus_api_mutex; to the DBusConnectionManager class and use it to protect both writes in this function and reads in all other functions that access s_dbus_api.
All reads from s_dbus_api elsewhere must also acquire this lock. For example:
bool DBusConnectionManager::isConnected_unlocked() const {
#ifndef HAVE_DBUS
return false;
#else
std::lock_guard<std::mutex> lock(s_dbus_api_mutex);
return m_connected && m_connection &&
s_dbus_api.connection_get_is_connected(m_connection.get());
#endif
} std::lock_guard<std::mutex> lock(s_dbus_api_mutex);
s_dbus_api = api;| } | ||
|
|
||
| // RAII deleters implementation | ||
| #include "mpris/DBusConnectionManager.h" |
There was a problem hiding this comment.
For better code organization and maintainability, it's recommended to place all #include directives at the top of the file. This include appears to be required for the DBusConnectionDeleter implementation, but it should be moved to the top of the file with the other includes to make dependencies clearer.
DBusConnectionManagerto use an injectedDBusAPIWrapperto allow for mocking libdbus C functions during unit testing.test_dbus_connection_manager_comprehensive.cppto correctly inject a custom C-API mock that reads/writes the mock state fromMockDBusConnection.unrefConnectionviaMPRISTypes.cppDBusConnectionDeleterto use the mocked API cleanly.PR created automatically by Jules for task 17988237554627425649 started by @segin