1 #pragma once 2 3 #include "types/duration_types.hpp" 4 #include "utils/set_exception.hpp" 5 6 #include <sdbusplus/asio/object_server.hpp> 7 #include <sdbusplus/asio/property.hpp> 8 9 #include <future> 10 #include <thread> 11 12 #include <gmock/gmock.h> 13 14 class DbusEnvironment : public ::testing::Environment 15 { 16 public: 17 ~DbusEnvironment(); 18 19 void SetUp() override; 20 void TearDown() override; 21 22 static boost::asio::io_context& getIoc(); 23 static std::shared_ptr<sdbusplus::asio::connection> getBus(); 24 static std::shared_ptr<sdbusplus::asio::object_server> getObjServer(); 25 static const char* serviceName(); 26 static std::function<void()> setPromise(std::string_view name); 27 static void sleepFor(Milliseconds); 28 static Milliseconds measureTime(std::function<void()>); 29 synchronizeIoc()30 static void synchronizeIoc() 31 { 32 while (ioc.poll() > 0) 33 {} 34 } 35 36 template <class Functor> synchronizedPost(Functor && functor)37 static void synchronizedPost(Functor&& functor) 38 { 39 boost::asio::post(ioc, std::forward<Functor>(functor)); 40 synchronizeIoc(); 41 } 42 43 template <class T, class F> waitForFutures(std::vector<std::future<T>> futures,T init,F && accumulator,Milliseconds timeout=std::chrono::seconds (10))44 static T waitForFutures(std::vector<std::future<T>> futures, T init, 45 F&& accumulator, 46 Milliseconds timeout = std::chrono::seconds(10)) 47 { 48 constexpr auto precission = Milliseconds(10); 49 auto elapsed = Milliseconds(0); 50 51 auto sum = init; 52 for (auto& future : futures) 53 { 54 while (future.valid() && elapsed < timeout) 55 { 56 synchronizeIoc(); 57 58 if (future.wait_for(precission) == std::future_status::ready) 59 { 60 sum = accumulator(sum, future.get()); 61 } 62 else 63 { 64 elapsed += precission; 65 } 66 } 67 } 68 69 if (elapsed >= timeout) 70 { 71 throw std::runtime_error("Timed out while waiting for future"); 72 } 73 74 return sum; 75 } 76 77 template <class T> waitForFuture(std::future<T> future,Milliseconds timeout=std::chrono::seconds (10))78 static T waitForFuture(std::future<T> future, 79 Milliseconds timeout = std::chrono::seconds(10)) 80 { 81 std::vector<std::future<T>> futures; 82 futures.emplace_back(std::move(future)); 83 84 return waitForFutures(std::move(futures), T{}, 85 [](auto, const auto& value) { return value; }, 86 timeout); 87 } 88 89 static bool waitForFuture(std::string_view name, 90 Milliseconds timeout = std::chrono::seconds(10)); 91 92 static bool waitForFutures(std::string_view name, 93 Milliseconds timeout = std::chrono::seconds(10)); 94 95 template <class T> getProperty(const std::string & path,const std::string & interfaceName,const std::string & property)96 static T getProperty(const std::string& path, 97 const std::string& interfaceName, 98 const std::string& property) 99 { 100 auto propertyPromise = std::promise<T>(); 101 auto propertyFuture = propertyPromise.get_future(); 102 sdbusplus::asio::getProperty<T>( 103 *DbusEnvironment::getBus(), DbusEnvironment::serviceName(), path, 104 interfaceName, property, 105 [&propertyPromise](const boost::system::error_code& ec, T t) { 106 if (ec) 107 { 108 utils::setException(propertyPromise, "GetProperty failed"); 109 return; 110 } 111 propertyPromise.set_value(t); 112 }); 113 return DbusEnvironment::waitForFuture(std::move(propertyFuture)); 114 } 115 116 template <class T> 117 static boost::system::error_code setProperty(const std::string & path,const std::string & interfaceName,const std::string & property,const T & newValue)118 setProperty(const std::string& path, const std::string& interfaceName, 119 const std::string& property, const T& newValue) 120 { 121 auto promise = std::promise<boost::system::error_code>(); 122 auto future = promise.get_future(); 123 sdbusplus::asio::setProperty( 124 *DbusEnvironment::getBus(), DbusEnvironment::serviceName(), path, 125 interfaceName, property, std::move(newValue), 126 [promise = std::move(promise)]( 127 boost::system::error_code ec) mutable { 128 promise.set_value(ec); 129 }); 130 return DbusEnvironment::waitForFuture(std::move(future)); 131 } 132 133 template <class... Args> 134 static boost::system::error_code callMethod(const std::string & path,const std::string & interface,const std::string & method,Args &&...args)135 callMethod(const std::string& path, const std::string& interface, 136 const std::string& method, Args&&... args) 137 { 138 auto promise = std::promise<boost::system::error_code>(); 139 auto future = promise.get_future(); 140 DbusEnvironment::getBus()->async_method_call( 141 [promise = std::move(promise)]( 142 boost::system::error_code ec) mutable { 143 promise.set_value(ec); 144 }, 145 DbusEnvironment::serviceName(), path, interface, method, 146 std::forward<Args>(args)...); 147 return DbusEnvironment::waitForFuture(std::move(future)); 148 } 149 150 private: 151 static std::future<bool> getFuture(std::string_view name); 152 153 static boost::asio::io_context ioc; 154 static std::shared_ptr<sdbusplus::asio::connection> bus; 155 static std::shared_ptr<sdbusplus::asio::object_server> objServer; 156 static std::map<std::string, std::vector<std::future<bool>>> futures; 157 static bool setUp; 158 }; 159