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