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