blob: 5c3bdb140ed7f94ee7f58c42f0d83eed530f92ca [file] [log] [blame]
Matt Spinler09d64002019-09-11 14:29:46 -05001#include "extensions/openpower-pels/data_interface.hpp"
Matt Spinlerf60ac272019-12-11 13:47:50 -06002#include "extensions/openpower-pels/host_interface.hpp"
3
4#include <fcntl.h>
5
6#include <filesystem>
7#include <sdeventplus/source/io.hpp>
Matt Spinler09d64002019-09-11 14:29:46 -05008
9#include <gmock/gmock.h>
10
11namespace openpower
12{
13namespace pels
14{
15
16class MockDataInterface : public DataInterfaceBase
17{
18 public:
19 MockDataInterface()
20 {
21 }
Matt Spinlerf60ac272019-12-11 13:47:50 -060022 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));
Matt Spinler677381b2020-01-23 10:04:29 -060026 MOCK_METHOD(std::string, getBMCFWVersionID, (), (const override));
Matt Spinler24a85582020-01-27 16:40:21 -060027 MOCK_METHOD(bool, getHostPELEnablement, (), (const override));
Matt Spinlerf60ac272019-12-11 13:47:50 -060028
29 void changeHostState(bool newState)
30 {
31 setHostState(newState);
32 }
33
34 void setHMCManaged(bool managed)
35 {
36 _hmcManaged = managed;
37 }
38};
39
40/**
41 * @brief The mock HostInterface class
Matt Spinler5342d9a2019-12-12 11:03:39 -060042 *
43 * This replaces the PLDM calls with a FIFO for the asynchronous
44 * responses.
Matt Spinlerf60ac272019-12-11 13:47:50 -060045 */
46class MockHostInterface : public HostInterface
47{
48 public:
Matt Spinler5342d9a2019-12-12 11:03:39 -060049 /**
50 * @brief Constructor
51 *
52 * @param[in] event - The sd_event object
53 * @param[in] dataIface - The DataInterface class
54 */
Matt Spinlerf60ac272019-12-11 13:47:50 -060055 MockHostInterface(sd_event* event, DataInterfaceBase& dataIface) :
56 HostInterface(event, dataIface)
57 {
Matt Spinler5342d9a2019-12-12 11:03:39 -060058 char templ[] = "/tmp/cmdfifoXXXXXX";
59 std::filesystem::path dir = mkdtemp(templ);
60 _fifo = dir / "fifo";
Matt Spinlerf60ac272019-12-11 13:47:50 -060061 }
62
Matt Spinler5342d9a2019-12-12 11:03:39 -060063 /**
64 * @brief Destructor
65 */
Matt Spinlerf60ac272019-12-11 13:47:50 -060066 virtual ~MockHostInterface()
67 {
Matt Spinler5342d9a2019-12-12 11:03:39 -060068 std::filesystem::remove_all(_fifo.parent_path());
Matt Spinlerf60ac272019-12-11 13:47:50 -060069 }
70
71 MOCK_METHOD(CmdStatus, sendNewLogCmd, (uint32_t, uint32_t), (override));
72
Matt Spinler5342d9a2019-12-12 11:03:39 -060073 /**
74 * @brief Cancels waiting for a command response
75 */
76 virtual void cancelCmd() override
77 {
78 _inProgress = false;
79 _source = nullptr;
80 }
81
82 /**
83 * @brief Returns the amount of time to wait before retrying after
84 * a failed send command.
85 *
86 * @return milliseconds - The amount of time to wait
87 */
88 virtual std::chrono::milliseconds getSendRetryDelay() const override
89 {
90 return std::chrono::milliseconds(2);
91 }
92
93 /**
94 * @brief Returns the amount of time to wait before retrying after
95 * a command receive.
96 *
97 * @return milliseconds - The amount of time to wait
98 */
99 virtual std::chrono::milliseconds getReceiveRetryDelay() const override
100 {
101 return std::chrono::milliseconds(2);
102 }
103
104 /**
Matt Spinler41293cb2019-12-12 13:11:09 -0600105 * @brief Returns the amount of time to wait before retrying if the
106 * host firmware's PEL storage was full and it can't store
107 * any more logs until it is freed up somehow.
108 *
109 * @return milliseconds - The amount of time to wait
110 */
111 virtual std::chrono::milliseconds getHostFullRetryDelay() const override
112 {
Matt Spinler6aae6a02020-02-07 12:46:07 -0600113 return std::chrono::milliseconds(400);
Matt Spinler41293cb2019-12-12 13:11:09 -0600114 }
115
116 /**
Matt Spinler5342d9a2019-12-12 11:03:39 -0600117 * @brief Returns the number of commands processed
118 */
119 size_t numCmdsProcessed() const
120 {
121 return _cmdsProcessed;
122 }
123
124 /**
125 * @brief Writes the data passed in to the FIFO
126 *
127 * @param[in] hostResponse - use a 0 to indicate success
128 *
129 * @return CmdStatus - success or failure
130 */
131 CmdStatus send(uint8_t hostResponse)
132 {
133 // Create a FIFO once.
134 if (!std::filesystem::exists(_fifo))
135 {
136 if (mkfifo(_fifo.c_str(), 0622))
137 {
138 ADD_FAILURE() << "Failed mkfifo " << _fifo << strerror(errno);
139 exit(-1);
140 }
141 }
142
143 // Open it and register the reponse callback to
144 // be used on FD activity.
145 int fd = open(_fifo.c_str(), O_NONBLOCK | O_RDWR);
146 EXPECT_TRUE(fd >= 0) << "Unable to open FIFO";
147
148 auto callback = [this](sdeventplus::source::IO& source, int fd,
149 uint32_t events) {
150 this->receive(source, fd, events);
151 };
152
153 try
154 {
155 _source = std::make_unique<sdeventplus::source::IO>(
156 _event, fd, EPOLLIN,
157 std::bind(callback, std::placeholders::_1,
158 std::placeholders::_2, std::placeholders::_3));
159 }
160 catch (std::exception& e)
161 {
162 ADD_FAILURE() << "Event exception: " << e.what();
163 close(fd);
164 return CmdStatus::failure;
165 }
166
167 // Write the fake host reponse to the FIFO
168 auto bytesWritten = write(fd, &hostResponse, sizeof(hostResponse));
169 EXPECT_EQ(bytesWritten, sizeof(hostResponse));
170
171 _inProgress = true;
172
173 return CmdStatus::success;
174 }
175
Matt Spinlerf60ac272019-12-11 13:47:50 -0600176 protected:
Matt Spinler5342d9a2019-12-12 11:03:39 -0600177 /**
178 * @brief Reads the data written to the fifo and then calls
179 * the subscriber's callback.
180 *
181 * Nonzero data indicates a command failure (for testing bad path).
182 *
183 * @param[in] source - The event source object
184 * @param[in] fd - The file descriptor used
185 * @param[in] events - The event bits
186 */
Matt Spinlerf60ac272019-12-11 13:47:50 -0600187 void receive(sdeventplus::source::IO& source, int fd,
188 uint32_t events) override
189 {
Matt Spinler5342d9a2019-12-12 11:03:39 -0600190 if (!(events & EPOLLIN))
191 {
192 return;
193 }
194
195 _inProgress = false;
196
197 int newFD = open(_fifo.c_str(), O_NONBLOCK | O_RDONLY);
198 ASSERT_TRUE(newFD >= 0) << "Failed to open FIFO";
199
200 // Read the host success/failure response from the FIFO.
201 uint8_t data;
202 auto bytesRead = read(newFD, &data, sizeof(data));
203 EXPECT_EQ(bytesRead, sizeof(data));
204
205 close(newFD);
206
207 ResponseStatus status = ResponseStatus::success;
208 if (data != 0)
209 {
210 status = ResponseStatus::failure;
211 }
212
213 if (_responseFunc)
214 {
215 (*_responseFunc)(status);
216 }
217
Matt Spinlerf60ac272019-12-11 13:47:50 -0600218 // Keep account of the number of commands responses for testing.
219 _cmdsProcessed++;
220 }
221
222 private:
Matt Spinler5342d9a2019-12-12 11:03:39 -0600223 /**
224 * @brief The event source for the fifo
225 */
226 std::unique_ptr<sdeventplus::source::IO> _source;
227
228 /**
229 * @brief the path to the fifo
230 */
231 std::filesystem::path _fifo;
232
233 /**
234 * @brief The number of commands processed
235 */
Matt Spinlerf60ac272019-12-11 13:47:50 -0600236 size_t _cmdsProcessed = 0;
Matt Spinler09d64002019-09-11 14:29:46 -0500237};
238
239} // namespace pels
240} // namespace openpower