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