xref: /openbmc/telemetry/tests/src/dbus_environment.hpp (revision 95f77e623c1b84663fe62e4506d2291b6919a13d)
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