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