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