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