#pragma once #include "types/duration_types.hpp" #include "utils/set_exception.hpp" #include #include #include #include #include class DbusEnvironment : public ::testing::Environment { public: ~DbusEnvironment(); void SetUp() override; void TearDown() override; void teardown(); static boost::asio::io_context& getIoc(); static std::shared_ptr getBus(); static std::shared_ptr getObjServer(); static const char* serviceName(); static std::function setPromise(std::string_view name); static void sleepFor(Milliseconds); static Milliseconds measureTime(std::function); static void synchronizeIoc() { while (ioc.poll() > 0) {} } template static void synchronizedPost(Functor&& functor) { boost::asio::post(ioc, std::forward(functor)); synchronizeIoc(); } template static T waitForFutures(std::vector> futures, T init, F&& accumulator, Milliseconds timeout = std::chrono::seconds(10)) { constexpr auto precission = Milliseconds(10); auto elapsed = Milliseconds(0); auto sum = init; for (auto& future : futures) { while (future.valid() && elapsed < timeout) { synchronizeIoc(); if (future.wait_for(precission) == std::future_status::ready) { sum = accumulator(sum, future.get()); } else { elapsed += precission; } } } if (elapsed >= timeout) { throw std::runtime_error("Timed out while waiting for future"); } return sum; } template static T waitForFuture(std::future future, Milliseconds timeout = std::chrono::seconds(10)) { std::vector> futures; futures.emplace_back(std::move(future)); return waitForFutures( std::move(futures), T{}, [](auto, const auto& value) { return value; }, timeout); } static bool waitForFuture(std::string_view name, Milliseconds timeout = std::chrono::seconds(10)); static bool waitForFutures(std::string_view name, Milliseconds timeout = std::chrono::seconds(10)); template static T getProperty(const std::string& path, const std::string& interfaceName, const std::string& property) { auto propertyPromise = std::promise(); auto propertyFuture = propertyPromise.get_future(); sdbusplus::asio::getProperty( *DbusEnvironment::getBus(), DbusEnvironment::serviceName(), path, interfaceName, property, [&propertyPromise](const boost::system::error_code& ec, T t) { if (ec) { utils::setException(propertyPromise, "GetProperty failed"); return; } propertyPromise.set_value(t); }); return DbusEnvironment::waitForFuture(std::move(propertyFuture)); } template static boost::system::error_code setProperty(const std::string& path, const std::string& interfaceName, const std::string& property, const T& newValue) { auto promise = std::promise(); auto future = promise.get_future(); sdbusplus::asio::setProperty( *DbusEnvironment::getBus(), DbusEnvironment::serviceName(), path, interfaceName, property, std::move(newValue), [promise = std::move(promise)]( boost::system::error_code ec) mutable { promise.set_value(ec); }); return DbusEnvironment::waitForFuture(std::move(future)); } template static boost::system::error_code callMethod(const std::string& path, const std::string& interface, const std::string& method, Args&&... args) { auto promise = std::promise(); auto future = promise.get_future(); DbusEnvironment::getBus()->async_method_call( [promise = std::move(promise)]( boost::system::error_code ec) mutable { promise.set_value(ec); }, DbusEnvironment::serviceName(), path, interface, method, std::forward(args)...); return DbusEnvironment::waitForFuture(std::move(future)); } private: static std::future getFuture(std::string_view name); static boost::asio::io_context ioc; static std::shared_ptr bus; static std::shared_ptr objServer; static std::map>> futures; static bool setUp; };