| /* | |
| * SPDX-FileCopyrightText: Copyright OpenBMC Authors | |
| * SPDX-License-Identifier: Apache-2.0 | |
| */ | |
| #pragma once | |
| #include "MctpRequester.hpp" | |
| #include <boost/asio/io_context.hpp> | |
| #include <boost/system/error_code.hpp> | |
| #include <sdbusplus/asio/connection.hpp> | |
| #include <sdbusplus/asio/object_server.hpp> | |
| #include <sdbusplus/asio/property.hpp> | |
| #include <sdbusplus/exception.hpp> | |
| #include <cerrno> | |
| #include <chrono> | |
| #include <functional> | |
| #include <memory> | |
| #include <optional> | |
| #include <string> | |
| #include <utility> | |
| #include <gtest/gtest.h> | |
| /** | |
| * Owns the private dbus-daemon and the shared bus objects for the whole | |
| * binary. Bring-up failure is not fatal: DbusMockTestBase fixtures skip | |
| * themselves, so pure-unit tests in the same binary still run. | |
| */ | |
| class DbusEnvironment : public ::testing::Environment | |
| { | |
| public: | |
| void SetUp() override; | |
| void TearDown() override; | |
| }; | |
| // Base fixture for tests that need the shared D-Bus session. | |
| class DbusMockTestBase : public ::testing::Test | |
| { | |
| protected: | |
| // Valid only while hasBus(). | |
| static std::shared_ptr<sdbusplus::asio::connection>& bus(); | |
| static sdbusplus::asio::object_server& objects(); | |
| static mctp::MctpRequester& requester(); | |
| static boost::asio::io_context& ioContext(); | |
| static bool hasBus(); | |
| // GTEST_SKIP() only returns from this function; derived overrides must | |
| // check testing::Test::IsSkipped() after delegating here. | |
| void SetUp() override; | |
| // Drains pending D-Bus work so the next test starts clean. | |
| void TearDown() override; | |
| // Run already-queued handlers so destructor-cancelled async ops finish. | |
| static void drainPendingAsync(); | |
| /** | |
| * Read a property from this connection's own object server. Async + | |
| * io_context pump because sd-bus rejects self-directed synchronous calls | |
| * with ELOOP. Throws SdBusError on error or timeout. | |
| */ | |
| template <typename PropertyType> | |
| static PropertyType getProperty(const std::string& path, | |
| const std::string& iface, | |
| const std::string& prop) | |
| { | |
| // Heap-owned handler state: a late reply may fire in a later pump, | |
| // after this frame has thrown ETIMEDOUT and unwound. | |
| struct State | |
| { | |
| std::optional<boost::system::error_code> ec; | |
| PropertyType value{}; | |
| }; | |
| auto state = std::make_shared<State>(); | |
| sdbusplus::asio::getProperty<PropertyType>( | |
| *bus(), bus()->get_unique_name(), path, iface, prop, | |
| [state](boost::system::error_code ec, PropertyType value) { | |
| state->value = std::move(value); | |
| state->ec = ec; | |
| }); | |
| if (!pumpIoUntil([state] { return state->ec.has_value(); }, | |
| propertyReadTimeout)) | |
| { | |
| throw sdbusplus::exception::SdBusError(ETIMEDOUT, | |
| "getProperty timed out"); | |
| } | |
| const boost::system::error_code callError = | |
| state->ec.value_or(boost::system::error_code{}); | |
| if (callError) | |
| { | |
| throw sdbusplus::exception::SdBusError( | |
| callError.value(), | |
| ("getProperty: " + callError.message()).c_str()); | |
| } | |
| return std::move(state->value); | |
| } | |
| // Pump the shared io_context until done() holds; false on timeout. Needed | |
| // by tests that wait on a timer inside the object under test. | |
| static bool pumpIoUntil(const std::function<bool()>& done, | |
| std::chrono::seconds timeout); | |
| private: | |
| friend class DbusEnvironment; | |
| static constexpr std::chrono::seconds propertyReadTimeout{5}; | |
| static bool tryConnect(); | |
| // Fresh instance per environment SetUp, never deleted: a late | |
| // ~connection() must not touch a destroyed io_uring service, and a | |
| // recreated environment must never run the previous lifetime's queued | |
| // raw-`this` handlers. | |
| static boost::asio::io_context* io; | |
| static std::shared_ptr<sdbusplus::asio::connection> conn; | |
| static std::unique_ptr<sdbusplus::asio::object_server> objectServer; | |
| static std::unique_ptr<mctp::MctpRequester> mctpRequester; | |
| static bool available; | |
| static std::string bringUpFailure; | |
| }; |