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