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