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