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