blob: 3890bc849ee408e0b8380a2332ede98f1da9cba4 [file] [log] [blame]
Patrick Venture123b5c02019-03-05 14:01:00 -08001#include "internal_sys_mock.hpp"
2
Willy Tu13224372024-07-15 17:00:15 +00003#include <linux/ipmi.h>
4#include <sys/ioctl.h>
5
Patrick Venture123b5c02019-03-05 14:01:00 -08006#include <ipmiblob/ipmi_errors.hpp>
7#include <ipmiblob/ipmi_handler.hpp>
8
Willy Tu13224372024-07-15 17:00:15 +00009#include <chrono>
10#include <cstring>
11#include <thread>
12
Patrick Venture1470bec2019-03-06 07:33:12 -080013namespace ipmiblob
Patrick Venture123b5c02019-03-05 14:01:00 -080014{
15
Willy Tu13224372024-07-15 17:00:15 +000016using std::chrono::milliseconds;
Patrick Venture123b5c02019-03-05 14:01:00 -080017using ::testing::_;
Willy Tu13224372024-07-15 17:00:15 +000018using ::testing::DoAll;
19using ::testing::ElementsAre;
Patrick Venture123b5c02019-03-05 14:01:00 -080020using ::testing::Return;
21
Willy Tu13224372024-07-15 17:00:15 +000022ACTION_TEMPLATE(SetArgNPointeeTo, HAS_1_TEMPLATE_PARAMS(unsigned, uIndex),
23 AND_2_VALUE_PARAMS(pData, uiDataSize))
24{
25 ipmi_recv* reply = reinterpret_cast<ipmi_recv*>(std::get<uIndex>(args));
26 std::memcpy(reply->msg.data, pData, uiDataSize);
27 reply->msg.data_len = uiDataSize;
28}
29
30ACTION_TEMPLATE(SetOpenDelays, HAS_1_TEMPLATE_PARAMS(unsigned, delay),
31 AND_0_VALUE_PARAMS())
32{
33 std::this_thread::sleep_for(milliseconds(delay));
34}
35
36class IpmiHandlerTest : public ::testing::Test
37{
38 protected:
39 IpmiHandlerTest() : sysMock(std::make_unique<internal::InternalSysMock>())
40 {}
41
42 void ExpectOpenError(IpmiHandler& ipmi, std::string_view msg)
43 {
44 EXPECT_THROW(
45 {
46 try
47 {
48 ipmi.open();
49 }
50 catch (const IpmiException& e)
51 {
52 // and this tests that it has the correct message
53 EXPECT_STREQ(msg.data(), e.what());
54 throw;
55 }
56 },
57 IpmiException);
58 }
59
60 void ExpectSendPacketError(IpmiHandler& ipmi, std::string_view msg)
61 {
62 EXPECT_THROW(
63 {
64 try
65 {
66 ipmi.sendPacket(0, 0, data);
67 }
68 catch (const IpmiException& e)
69 {
70 // and this tests that it has the correct message
71 EXPECT_STREQ(msg.data(), e.what());
72 throw;
73 }
74 },
75 IpmiException);
76 }
77
78 std::unique_ptr<internal::InternalSysMock> sysMock;
79 std::vector<std::uint8_t> data;
80 int fd = 1;
81 int badFd = -1;
82};
83
84TEST_F(IpmiHandlerTest, OpenAllFails)
Patrick Venture123b5c02019-03-05 14:01:00 -080085{
86 /* Open against all device files fail. */
Willy Tu13224372024-07-15 17:00:15 +000087 EXPECT_CALL(*sysMock, open(_, _)).WillRepeatedly(Return(badFd));
Patrick Venture123b5c02019-03-05 14:01:00 -080088
Patrick Venture92a4a622021-02-04 10:45:00 -080089 IpmiHandler ipmi(std::move(sysMock));
Willy Tu13224372024-07-15 17:00:15 +000090 ExpectOpenError(ipmi, "Unable to open any ipmi devices");
91}
92
93TEST_F(IpmiHandlerTest, OpenSucess)
94{
95 // Make sure that we don't throw any exception when getting bad file
96 // descriptor Return valid file descriptor at the last ipmi device for a
97 // succeful open().
98 EXPECT_CALL(*sysMock, open(_, _))
99 .WillOnce(Return(badFd))
100 .WillOnce(Return(badFd))
101 .WillOnce(Return(fd));
102
103 IpmiHandler ipmi(std::move(sysMock));
104 EXPECT_NO_THROW(ipmi.open());
105}
106
107TEST_F(IpmiHandlerTest, SendPacketOpenAllFails)
108{
109 EXPECT_CALL(*sysMock, open(_, _)).WillRepeatedly(Return(badFd));
110
111 IpmiHandler ipmi(std::move(sysMock));
112 ExpectSendPacketError(ipmi, "Unable to open any ipmi devices");
113 ExpectSendPacketError(ipmi, "Unable to open any ipmi devices");
114}
115
116TEST_F(IpmiHandlerTest, SendPacketRequestFailed)
117{
118 EXPECT_CALL(*sysMock, open(_, _)).WillOnce(Return(fd));
119 EXPECT_CALL(*sysMock, ioctl(fd, IPMICTL_SEND_COMMAND, _))
120 .WillOnce(Return(badFd));
121
122 IpmiHandler ipmi(std::move(sysMock));
123 ExpectSendPacketError(ipmi, "Unable to send IPMI request.");
124}
125
126TEST_F(IpmiHandlerTest, SendPacketPollingError)
127{
128 EXPECT_CALL(*sysMock, open(_, _)).WillOnce(Return(fd));
129 EXPECT_CALL(*sysMock, ioctl(fd, IPMICTL_SEND_COMMAND, _))
130 .WillOnce(Return(0));
131 EXPECT_CALL(*sysMock, poll(_, 1, _)).WillOnce(Return(badFd));
132
133 IpmiHandler ipmi(std::move(sysMock));
134 ExpectSendPacketError(ipmi, "Polling Error occurred.");
135}
136
137TEST_F(IpmiHandlerTest, SendPacketPollTimeout)
138{
139 EXPECT_CALL(*sysMock, open(_, _)).WillOnce(Return(fd));
140 EXPECT_CALL(*sysMock, ioctl(fd, IPMICTL_SEND_COMMAND, _))
141 .WillOnce(Return(0));
142 EXPECT_CALL(*sysMock, poll(_, 1, _)).WillOnce(Return(0));
143
144 IpmiHandler ipmi(std::move(sysMock));
145 ExpectSendPacketError(ipmi, "Timeout waiting for reply.");
146}
147
148TEST_F(IpmiHandlerTest, SendPacketBadIpmiReply)
149{
150 EXPECT_CALL(*sysMock, open(_, _)).WillOnce(Return(fd));
151 EXPECT_CALL(*sysMock, ioctl(fd, IPMICTL_SEND_COMMAND, _))
152 .WillOnce(Return(0));
153 EXPECT_CALL(*sysMock, poll(_, 1, _)).WillOnce(Return(1));
154 EXPECT_CALL(*sysMock, ioctl(fd, IPMICTL_RECEIVE_MSG_TRUNC, _))
155 .WillOnce(Return(badFd));
156
157 IpmiHandler ipmi(std::move(sysMock));
158 ExpectSendPacketError(ipmi, "Unable to read reply.");
159}
160
161TEST_F(IpmiHandlerTest, SendPacketNotIpmiOk)
162{
163 EXPECT_CALL(*sysMock, open(_, _)).WillOnce(Return(fd));
164 EXPECT_CALL(*sysMock, ioctl(fd, IPMICTL_SEND_COMMAND, _))
165 .WillOnce(Return(0));
166 EXPECT_CALL(*sysMock, poll(_, 1, _)).WillOnce(Return(1));
167
168 std::vector<std::uint8_t> expectedOutput = {2};
169
170 EXPECT_CALL(*sysMock, ioctl(fd, IPMICTL_RECEIVE_MSG_TRUNC, _))
171 .WillOnce(DoAll(
172 SetArgNPointeeTo<2>(expectedOutput.data(), expectedOutput.size()),
173 Return(0)));
174
175 IpmiHandler ipmi(std::move(sysMock));
176 ExpectSendPacketError(ipmi, "Received IPMI_CC: 2");
177}
178
179TEST_F(IpmiHandlerTest, SendPacketFailedOpenOnce)
180{
181 EXPECT_CALL(*sysMock, open(_, _))
182 .WillOnce(Return(badFd))
183 .WillOnce(Return(badFd))
184 .WillOnce(Return(badFd))
185 .WillOnce(Return(fd));
186 EXPECT_CALL(*sysMock, ioctl(fd, IPMICTL_SEND_COMMAND, _))
187 .WillOnce(Return(0));
188 EXPECT_CALL(*sysMock, poll(_, 1, _)).WillOnce(Return(1));
189
190 std::vector<std::uint8_t> expectedOutput = {0, 1, 2, 3};
191
192 EXPECT_CALL(*sysMock, ioctl(fd, IPMICTL_RECEIVE_MSG_TRUNC, _))
193 .WillOnce(DoAll(
194 SetArgNPointeeTo<2>(expectedOutput.data(), expectedOutput.size()),
195 Return(0)));
196
197 IpmiHandler ipmi(std::move(sysMock));
198
199 ExpectSendPacketError(ipmi, "Unable to open any ipmi devices");
200 EXPECT_THAT(ipmi.sendPacket(0, 0, data), ElementsAre(1, 2, 3));
201}
202
203TEST_F(IpmiHandlerTest, SendPacketSucess)
204{
205 EXPECT_CALL(*sysMock, open(_, _)).WillOnce(Return(fd));
206 EXPECT_CALL(*sysMock, ioctl(fd, IPMICTL_SEND_COMMAND, _))
207 .WillOnce(Return(0));
208 EXPECT_CALL(*sysMock, poll(_, 1, _)).WillOnce(Return(1));
209
210 std::vector<std::uint8_t> expectedOutput = {0, 1, 2, 3};
211
212 EXPECT_CALL(*sysMock, ioctl(fd, IPMICTL_RECEIVE_MSG_TRUNC, _))
213 .WillOnce(DoAll(
214 SetArgNPointeeTo<2>(expectedOutput.data(), expectedOutput.size()),
215 Return(0)));
216
217 IpmiHandler ipmi(std::move(sysMock));
218 EXPECT_THAT(ipmi.sendPacket(0, 0, data), ElementsAre(1, 2, 3));
219}
220
221// Tried to call open() in different thread and making sure that both thread
222// tried it and there aree no data race. Expect the first thread to fail to
223// open() and second one pass open(), but failed in the IPMICTL_SEND_COMMAND.
224TEST_F(IpmiHandlerTest, SendPacketTriedOpenInParallel)
225{
226 EXPECT_CALL(*sysMock, open(_, _))
227 // The badFd is expected to be used in testOpenParallel0 and need enough
228 // delay to make sure that testOpenParallel1 starts before
229 // testOpenParallel0 finishes.
230 .WillOnce(DoAll(SetOpenDelays<10>(), Return(badFd)))
231 .WillOnce(DoAll(SetOpenDelays<10>(), Return(badFd)))
232 .WillOnce(DoAll(SetOpenDelays<10>(), Return(badFd)))
233 .WillOnce(Return(fd));
234 EXPECT_CALL(*sysMock, ioctl(fd, IPMICTL_SEND_COMMAND, _))
235 .WillOnce(Return(-1));
236
237 IpmiHandler ipmi(std::move(sysMock));
238 auto testOpenParallel0 = [this, &ipmi]() {
239 ExpectSendPacketError(ipmi, "Unable to open any ipmi devices");
240 };
241
242 auto testOpenParallel1 = [this, &ipmi]() {
243 // Make sure this start after testOpenParallel0 get to the open()
244 std::this_thread::sleep_for(milliseconds(10));
245 ExpectSendPacketError(ipmi, "Unable to send IPMI request.");
246 };
247
248 std::thread t1(testOpenParallel0);
249 std::thread t2(testOpenParallel1);
250 t1.join();
251 t2.join();
Patrick Venture123b5c02019-03-05 14:01:00 -0800252}
253
Patrick Venture1470bec2019-03-06 07:33:12 -0800254} // namespace ipmiblob