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