1 #include "extensions/openpower-pels/data_interface.hpp"
2 #include "extensions/openpower-pels/host_interface.hpp"
3 
4 #include <fcntl.h>
5 
6 #include <filesystem>
7 #include <sdeventplus/source/io.hpp>
8 
9 #include <gmock/gmock.h>
10 
11 namespace openpower
12 {
13 namespace pels
14 {
15 
16 class MockDataInterface : public DataInterfaceBase
17 {
18   public:
19     MockDataInterface()
20     {
21     }
22     MOCK_METHOD(std::string, getMachineTypeModel, (), (const override));
23     MOCK_METHOD(std::string, getMachineSerialNumber, (), (const override));
24     MOCK_METHOD(std::string, getServerFWVersion, (), (const override));
25     MOCK_METHOD(std::string, getBMCFWVersion, (), (const override));
26     MOCK_METHOD(std::string, getBMCFWVersionID, (), (const override));
27     MOCK_METHOD(bool, getHostPELEnablement, (), (const override));
28     MOCK_METHOD(std::string, getBMCState, (), (const override));
29     MOCK_METHOD(std::string, getChassisState, (), (const override));
30     MOCK_METHOD(std::string, getHostState, (), (const override));
31     MOCK_METHOD(std::string, getMotherboardCCIN, (), (const override));
32     MOCK_METHOD(void, getHWCalloutFields,
33                 (const std::string&, std::string&, std::string&, std::string&),
34                 (const override));
35     MOCK_METHOD(std::string, getLocationCode, (const std::string&),
36                 (const override));
37     MOCK_METHOD(std::vector<std::string>, getSystemNames, (), (const override));
38     MOCK_METHOD(std::string, expandLocationCode, (const std::string&, uint16_t),
39                 (const override));
40     MOCK_METHOD(std::string, getInventoryFromLocCode,
41                 (const std::string&, uint16_t, bool), (const override));
42     MOCK_METHOD(void, assertLEDGroup, (const std::string&, bool),
43                 (const override));
44     MOCK_METHOD(void, setFunctional, (const std::string&, bool),
45                 (const override));
46     MOCK_METHOD(std::vector<uint8_t>, getSystemIMKeyword, (), (const override));
47     MOCK_METHOD(bool, getQuiesceOnError, (), (const override));
48     MOCK_METHOD(void, setCriticalAssociation, (const std::string&),
49                 (const override));
50     MOCK_METHOD(std::vector<bool>, checkDumpStatus,
51                 (const std::vector<std::string>&), (const override));
52     MOCK_METHOD(std::string, getBootState, (), (const override));
53 
54     void changeHostState(bool newState)
55     {
56         setHostUp(newState);
57     }
58 
59     void setHMCManaged(bool managed)
60     {
61         _hmcManaged = managed;
62     }
63 };
64 
65 /**
66  * @brief The mock HostInterface class
67  *
68  * This replaces the PLDM calls with a FIFO for the asynchronous
69  * responses.
70  */
71 class MockHostInterface : public HostInterface
72 {
73   public:
74     /**
75      * @brief Constructor
76      *
77      * @param[in] event - The sd_event object
78      * @param[in] dataIface - The DataInterface class
79      */
80     MockHostInterface(sd_event* event, DataInterfaceBase& dataIface) :
81         HostInterface(event, dataIface)
82     {
83         char templ[] = "/tmp/cmdfifoXXXXXX";
84         std::filesystem::path dir = mkdtemp(templ);
85         _fifo = dir / "fifo";
86     }
87 
88     /**
89      * @brief Destructor
90      */
91     virtual ~MockHostInterface()
92     {
93         std::filesystem::remove_all(_fifo.parent_path());
94     }
95 
96     MOCK_METHOD(CmdStatus, sendNewLogCmd, (uint32_t, uint32_t), (override));
97 
98     /**
99      * @brief Cancels waiting for a command response
100      */
101     virtual void cancelCmd() override
102     {
103         _inProgress = false;
104         _source = nullptr;
105     }
106 
107     /**
108      * @brief Returns the amount of time to wait before retrying after
109      *        a failed send command.
110      *
111      * @return milliseconds - The amount of time to wait
112      */
113     virtual std::chrono::milliseconds getSendRetryDelay() const override
114     {
115         return std::chrono::milliseconds(2);
116     }
117 
118     /**
119      * @brief Returns the amount of time to wait before retrying after
120      *        a command receive.
121      *
122      * @return milliseconds - The amount of time to wait
123      */
124     virtual std::chrono::milliseconds getReceiveRetryDelay() const override
125     {
126         return std::chrono::milliseconds(2);
127     }
128 
129     /**
130      * @brief Returns the amount of time to wait before retrying if the
131      *        host firmware's PEL storage was full and it can't store
132      *        any more logs until it is freed up somehow.
133      *
134      * @return milliseconds - The amount of time to wait
135      */
136     virtual std::chrono::milliseconds getHostFullRetryDelay() const override
137     {
138         return std::chrono::milliseconds(400);
139     }
140 
141     /**
142      * @brief Returns the number of commands processed
143      */
144     size_t numCmdsProcessed() const
145     {
146         return _cmdsProcessed;
147     }
148 
149     /**
150      * @brief Writes the data passed in to the FIFO
151      *
152      * @param[in] hostResponse - use a 0 to indicate success
153      *
154      * @return CmdStatus - success or failure
155      */
156     CmdStatus send(uint8_t hostResponse)
157     {
158         // Create a FIFO once.
159         if (!std::filesystem::exists(_fifo))
160         {
161             if (mkfifo(_fifo.c_str(), 0622))
162             {
163                 ADD_FAILURE() << "Failed mkfifo " << _fifo << strerror(errno);
164                 exit(-1);
165             }
166         }
167 
168         // Open it and register the reponse callback to
169         // be used on FD activity.
170         int fd = open(_fifo.c_str(), O_NONBLOCK | O_RDWR);
171         EXPECT_TRUE(fd >= 0) << "Unable to open FIFO";
172 
173         auto callback = [this](sdeventplus::source::IO& source, int fd,
174                                uint32_t events) {
175             this->receive(source, fd, events);
176         };
177 
178         try
179         {
180             _source = std::make_unique<sdeventplus::source::IO>(
181                 _event, fd, EPOLLIN,
182                 std::bind(callback, std::placeholders::_1,
183                           std::placeholders::_2, std::placeholders::_3));
184         }
185         catch (const std::exception& e)
186         {
187             ADD_FAILURE() << "Event exception: " << e.what();
188             close(fd);
189             return CmdStatus::failure;
190         }
191 
192         // Write the fake host reponse to the FIFO
193         auto bytesWritten = write(fd, &hostResponse, sizeof(hostResponse));
194         EXPECT_EQ(bytesWritten, sizeof(hostResponse));
195 
196         _inProgress = true;
197 
198         return CmdStatus::success;
199     }
200 
201   protected:
202     /**
203      * @brief Reads the data written to the fifo and then calls
204      *        the subscriber's callback.
205      *
206      * Nonzero data indicates a command failure (for testing bad path).
207      *
208      * @param[in] source - The event source object
209      * @param[in] fd - The file descriptor used
210      * @param[in] events - The event bits
211      */
212     void receive(sdeventplus::source::IO& /*source*/, int /*fd*/,
213                  uint32_t events) override
214     {
215         if (!(events & EPOLLIN))
216         {
217             return;
218         }
219 
220         _inProgress = false;
221 
222         int newFD = open(_fifo.c_str(), O_NONBLOCK | O_RDONLY);
223         ASSERT_TRUE(newFD >= 0) << "Failed to open FIFO";
224 
225         // Read the host success/failure response from the FIFO.
226         uint8_t data;
227         auto bytesRead = read(newFD, &data, sizeof(data));
228         EXPECT_EQ(bytesRead, sizeof(data));
229 
230         close(newFD);
231 
232         ResponseStatus status = ResponseStatus::success;
233         if (data != 0)
234         {
235             status = ResponseStatus::failure;
236         }
237 
238         callResponseFunc(status);
239 
240         // Keep account of the number of commands responses for testing.
241         _cmdsProcessed++;
242     }
243 
244   private:
245     /**
246      * @brief The event source for the fifo
247      */
248     std::unique_ptr<sdeventplus::source::IO> _source;
249 
250     /**
251      * @brief the path to the fifo
252      */
253     std::filesystem::path _fifo;
254 
255     /**
256      * @brief The number of commands processed
257      */
258     size_t _cmdsProcessed = 0;
259 };
260 
261 } // namespace pels
262 } // namespace openpower
263